feat: release notes qbit (detection, drafting, approval, notification, What's new UI API) #1

Merged
KofTwentyTwo merged 5 commits from feature/release-notes into develop 2026-10-15 21:58:04 +00:00
KofTwentyTwo commented 2026-10-15 19:33:42 +00:00 (Migrated from github.com)

What

The first version of qbit-release-notes: customer-facing release notes for every release of a QQQ app (owner request 2026-10-15). Design: docs/SPEC.md.

  • Tables (release_notes_release, _item, _settings, _user_state): standard QQQ tables with a Liquibase changelog. Items reference their release by foreign key (ON DELETE CASCADE), which satisfies kof22's foreign-key guardrail. Settings are seeded insert-only with require approval, all users and notify.
  • Detection: runs once per version across pods and restarts, guarded by a unique (app, version) key. Pods that lose the insert race re-read and stop. Development versions are ignored. The host calls ReleaseNotesRuntime.of(qInstance).detectInBackground() after startup.
  • Sources (ReleaseSource SPI):
    • GitHubReleaseSource, TAG mode: releases after the previous version up to this one.
    • GitHubReleaseSource, WINDOW mode: releases published since the previous release, for a separately released frontend.
    • Both modes collect the PRs linked from the release bodies. The token is read from the environment at call time.
    • ComponentVersionsSource: qbit and platform versions that changed.
  • Drafting (ReleaseNotesDrafter SPI): a deterministic RuleBasedDrafter.
    • It uses Release notes: text written by authors, cleans PR titles and categorises from Conventional Commit types and labels.
    • It excludes internal work: chore/ci/build/test/refactor/docs, dependency and infrastructure changes, and skip labels.
    • ReleaseNotesSanitizer blocks PR and issue numbers, GitHub links, commit hashes, ticket keys, e-mail and IP addresses, internal hosts and secret-looking text.
    • Nothing is published automatically unless settings say AUTO_PUBLISH.
  • Actions: Publish, Skip and Redraft are record actions with the standard review and result screens; Check for new release is a table action. None of them are in the nav.
  • Publishing: sends one NotificationSignal through QAlerts.notify.
    • Category RELEASE, severity INFO, audience all users or a permission.
    • Link /app/whats-new?release=<id>. Source key release-notes:<app>:<version>, so a resend updates the existing notification instead of duplicating it.
    • Delivery honours each user's existing per-category notification preferences (mute, email opt-in) in qbit-alerts. No dependency on qbit-alerts.
  • UI API (release-notes 1.0.0): GET /qbit/release-notes/releases, GET /qbit/release-notes/summary and POST /qbit/release-notes/seen.
    • Only customer fields are returned.
    • The committed OpenAPI is covered by a contract test.
  • Help bundle: covers the app, tables, fields and processes.

Tests

./mvnw verify: 25 tests pass and the JaCoCo gate is met.

  • Once-per-version detection with 8 concurrent "pods".
  • The approval flow (publish, notification, What's new with the per-user unread indicator, mark seen).
  • Auto-publish, including auto-skip when a release has no customer-visible change.
  • Permission audiences.
  • Internal references blocking publish.
  • Redraft keeping hand-written items.
  • Drafting rules and the sanitizer.
  • The GitHub source against recorded payloads, including a stale higher tag and draft releases.
  • Changelog runs twice; help coverage; UI API contract.

After merge, the release captain cuts and tags 0.1.0-RC.1 (no release:rc label: that flow currently publishes without a tag or GitHub release).

🤖 Generated with Claude Code

## What The first version of `qbit-release-notes`: customer-facing release notes for every release of a QQQ app (owner request 2026-10-15). Design: `docs/SPEC.md`. - **Tables** (`release_notes_release`, `_item`, `_settings`, `_user_state`): standard QQQ tables with a Liquibase changelog. Items reference their release by foreign key (`ON DELETE CASCADE`), which satisfies kof22's foreign-key guardrail. Settings are seeded insert-only with require approval, all users and notify. - **Detection**: runs once per version across pods and restarts, guarded by a unique (app, version) key. Pods that lose the insert race re-read and stop. Development versions are ignored. The host calls `ReleaseNotesRuntime.of(qInstance).detectInBackground()` after startup. - **Sources** (`ReleaseSource` SPI): - `GitHubReleaseSource`, TAG mode: releases after the previous version up to this one. - `GitHubReleaseSource`, WINDOW mode: releases published since the previous release, for a separately released frontend. - Both modes collect the PRs linked from the release bodies. The token is read from the environment at call time. - `ComponentVersionsSource`: qbit and platform versions that changed. - **Drafting** (`ReleaseNotesDrafter` SPI): a deterministic `RuleBasedDrafter`. - It uses `Release notes:` text written by authors, cleans PR titles and categorises from Conventional Commit types and labels. - It excludes internal work: chore/ci/build/test/refactor/docs, dependency and infrastructure changes, and skip labels. - `ReleaseNotesSanitizer` blocks PR and issue numbers, GitHub links, commit hashes, ticket keys, e-mail and IP addresses, internal hosts and secret-looking text. - Nothing is published automatically unless settings say `AUTO_PUBLISH`. - **Actions**: Publish, Skip and Redraft are record actions with the standard review and result screens; Check for new release is a table action. None of them are in the nav. - **Publishing**: sends one `NotificationSignal` through `QAlerts.notify`. - Category `RELEASE`, severity INFO, audience all users or a permission. - Link `/app/whats-new?release=<id>`. Source key `release-notes:<app>:<version>`, so a resend updates the existing notification instead of duplicating it. - Delivery honours each user's existing per-category notification preferences (mute, email opt-in) in qbit-alerts. No dependency on qbit-alerts. - **UI API** (`release-notes` 1.0.0): `GET /qbit/release-notes/releases`, `GET /qbit/release-notes/summary` and `POST /qbit/release-notes/seen`. - Only customer fields are returned. - The committed OpenAPI is covered by a contract test. - **Help bundle**: covers the app, tables, fields and processes. ## Tests `./mvnw verify`: 25 tests pass and the JaCoCo gate is met. - Once-per-version detection with 8 concurrent "pods". - The approval flow (publish, notification, What's new with the per-user unread indicator, mark seen). - Auto-publish, including auto-skip when a release has no customer-visible change. - Permission audiences. - Internal references blocking publish. - Redraft keeping hand-written items. - Drafting rules and the sanitizer. - The GitHub source against recorded payloads, including a stale higher tag and draft releases. - Changelog runs twice; help coverage; UI API contract. After merge, the release captain cuts and tags 0.1.0-RC.1 (no `release:rc` label: that flow currently publishes without a tag or GitHub release). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.