Update (2026-10-14): pinned to QQQ 4.1.0-RC.7, which carries QRun-IO/qqq#1162, and merged with develop. The column is now changeset 29 (develop owns 21-28). The section also covers develop's per-user Slack: Slack mode and Slack from severity per category. Pause skips non-security Slack as well as email. The summary schema carries preferencesSection (alerts extension 1.1.0). 186 tests pass; JaCoCo 94.5% instructions, 100% classes.
Summary
This registers a Notifications section in QQQ's new per-user Preferences (QRun-IO/qqq#1162). Users can see and change their notification settings on the Preferences page, or from the gear in the bell (Next UI PR to follow).
Depends on QQQ #1162. It needs the QQQ RC that ships user preferences. Until then CI cannot compile against RC.4, so this stays a draft. When the RC is out, I'll pin qqq.snapshot.version to it in this PR. Locally it builds and passes against 4.1.0-PREFS-SNAPSHOT, which is #1162 installed.
The section
notifications ("Notifications"), provider AlertsNotificationPreferencesProvider. It needs permission alerts.view and has no app scope, so every user with the bell gets it. It can be switched off with AlertsQBitConfig.withPreferencesSectionEnabled(false).
Settings (from alerts_user_preference):
pause notifications until a time;
daily digest time (0 to 23, with labels) and time zone (validated with ZoneId);
"Show alerts in the app";
"Email me alerts".
Categories list (alerts_notification_preference, one row per category):
in-app on or off, and in-app from severity;
email off, immediate or daily digest, and email from severity.
SECURITY's in-app fields are read-only: "Security notices can't be muted."
Alert rules list: only the user's own USER subscriptions. Each row has enabled, from severity and delivery mode. A key that belongs to another user or a team is refused.
Behaviour changes
New column in_app_min_severity (changeset qbit-alerts:29). In-app recipients below the user's threshold are skipped, but SECURITY notices are always delivered.
While pausedUntil is in the future, non-security emails (immediate and digest) are skipped. The notifications stay in the inbox, and a later digest picks them up.
The inbox summary returns preferencesSection: "notifications".
The preference tables have FIELD-level audits, so audits record the user and the client channel.
Round trip through UserPreferencesAction: defaults; patching scalars, categories and subscriptions; reload; table rows; audits.
Security: user A's changes leave B's rows alone; B sees only B's subscriptions; another user's or a team's subscription key gets 400; the SECURITY row is read-only; a bad time zone gets 400; the section is hidden without alerts.view.
inAppMinSeverity filtering, the pause email skip (SECURITY still sends), and the summary shortcut.
kof22
The alerts changelog (changeset 29) adds one nullable column. kof22 picks it up with the qbit bump, and no host changeset is needed.
**Update (2026-10-14):** pinned to QQQ 4.1.0-RC.7, which carries QRun-IO/qqq#1162, and merged with develop. The column is now changeset **29** (develop owns 21-28). The section also covers develop's per-user Slack: Slack mode and Slack from severity per category. Pause skips non-security Slack as well as email. The summary schema carries `preferencesSection` (alerts extension 1.1.0). 186 tests pass; JaCoCo 94.5% instructions, 100% classes.
## Summary
This registers a **Notifications** section in QQQ's new per-user Preferences (QRun-IO/qqq#1162). Users can see and change their notification settings on the Preferences page, or from the gear in the bell (Next UI PR to follow).
**Depends on QQQ #1162.** It needs the QQQ RC that ships user preferences. Until then CI cannot compile against RC.4, so this stays a draft. When the RC is out, I'll pin `qqq.snapshot.version` to it in this PR. Locally it builds and passes against `4.1.0-PREFS-SNAPSHOT`, which is #1162 installed.
### The section
`notifications` ("Notifications"), provider `AlertsNotificationPreferencesProvider`. It needs permission `alerts.view` and has no app scope, so every user with the bell gets it. It can be switched off with `AlertsQBitConfig.withPreferencesSectionEnabled(false)`.
- **Settings** (from `alerts_user_preference`):
- pause notifications until a time;
- daily digest time (0 to 23, with labels) and time zone (validated with `ZoneId`);
- "Show alerts in the app";
- "Email me alerts".
- **Categories** list (`alerts_notification_preference`, one row per category):
- in-app on or off, and in-app from severity;
- email off, immediate or daily digest, and email from severity.
- SECURITY's in-app fields are read-only: "Security notices can't be muted."
- **Alert rules** list: only the user's own `USER` subscriptions. Each row has enabled, from severity and delivery mode. A key that belongs to another user or a team is refused.
### Behaviour changes
- New column `in_app_min_severity` (changeset `qbit-alerts:29`). In-app recipients below the user's threshold are skipped, but SECURITY notices are always delivered.
- While `pausedUntil` is in the future, non-security emails (immediate and digest) are skipped. The notifications stay in the inbox, and a later digest picks them up.
- The inbox summary returns `preferencesSection: "notifications"`.
- The preference tables have FIELD-level audits, so audits record the user and the client channel.
## Tests
- 74 tests, 0 failures. JaCoCo 91.5% instructions, 99.2% classes. Checkstyle clean.
- Round trip through `UserPreferencesAction`: defaults; patching scalars, categories and subscriptions; reload; table rows; audits.
- Security: user A's changes leave B's rows alone; B sees only B's subscriptions; another user's or a team's subscription key gets 400; the SECURITY row is read-only; a bad time zone gets 400; the section is hidden without `alerts.view`.
- `inAppMinSeverity` filtering, the pause email skip (SECURITY still sends), and the summary shortcut.
## kof22
The alerts changelog (changeset 29) adds one nullable column. kof22 picks it up with the qbit bump, and no host changeset is needed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Update (2026-10-14): pinned to QQQ 4.1.0-RC.7, which carries QRun-IO/qqq#1162, and merged with develop. The column is now changeset 29 (develop owns 21-28). The section also covers develop's per-user Slack: Slack mode and Slack from severity per category. Pause skips non-security Slack as well as email. The summary schema carries
preferencesSection(alerts extension 1.1.0). 186 tests pass; JaCoCo 94.5% instructions, 100% classes.Summary
This registers a Notifications section in QQQ's new per-user Preferences (QRun-IO/qqq#1162). Users can see and change their notification settings on the Preferences page, or from the gear in the bell (Next UI PR to follow).
Depends on QQQ #1162. It needs the QQQ RC that ships user preferences. Until then CI cannot compile against RC.4, so this stays a draft. When the RC is out, I'll pin
qqq.snapshot.versionto it in this PR. Locally it builds and passes against4.1.0-PREFS-SNAPSHOT, which is #1162 installed.The section
notifications("Notifications"), providerAlertsNotificationPreferencesProvider. It needs permissionalerts.viewand has no app scope, so every user with the bell gets it. It can be switched off withAlertsQBitConfig.withPreferencesSectionEnabled(false).alerts_user_preference):ZoneId);alerts_notification_preference, one row per category):USERsubscriptions. Each row has enabled, from severity and delivery mode. A key that belongs to another user or a team is refused.Behaviour changes
in_app_min_severity(changesetqbit-alerts:29). In-app recipients below the user's threshold are skipped, but SECURITY notices are always delivered.pausedUntilis in the future, non-security emails (immediate and digest) are skipped. The notifications stay in the inbox, and a later digest picks them up.preferencesSection: "notifications".Tests
UserPreferencesAction: defaults; patching scalars, categories and subscriptions; reload; table rows; audits.alerts.view.inAppMinSeverityfiltering, the pause email skip (SECURITY still sends), and the summary shortcut.kof22
The alerts changelog (changeset 29) adds one nullable column. kof22 picks it up with the qbit bump, and no host changeset is needed.
🤖 Generated with Claude Code