feat: add release communications
All checks were successful
deploy-socialize / image (push) Successful in 1m12s
deploy-socialize / deploy (push) Successful in 19s

This commit is contained in:
2026-05-07 21:00:59 -04:00
parent 7a8a0a44bf
commit b6eb348605
61 changed files with 8594 additions and 4 deletions

View File

@@ -0,0 +1,280 @@
# Feature: Release Communications
## Status
Draft
## Goal
Give users a clear, curated view of product changes they have not seen yet, and give developers a back-office workflow for reconciling shipped commits with human-readable update entries.
The user-facing experience answers: "What changed since I last paid attention?"
The developer-facing experience answers: "Which shipped commits have been communicated, grouped, or intentionally marked internal-only?"
This feature is especially important during alpha and beta, where continuous delivery makes exact public version numbers less useful than a readable change history.
## User Stories
- As an authenticated user, I want to see new features, improvements, and fixes since my last visit so that product changes do not surprise me.
- As an organization owner, I want important updates surfaced clearly so that I can understand changes that may affect my team.
- As a developer, I want to create curated update entries so that users receive meaningful communication instead of raw commit logs.
- As a developer, I want to see shipped commits and attach them to update entries so that I can verify all relevant work has been communicated.
- As a developer, I want to mark commits as internal-only so that refactors, chores, and infrastructure work do not pollute the user-facing update feed.
- As a developer, I want optional email digests for inactive users so that alpha/beta users who do not log in still learn about important changes.
- As a developer, I want to manually send an email announcement for a published update so that I can deliberately announce important alpha/beta changes.
## Product Model
### Release Update
A release update is a curated, user-facing communication entry.
Fields:
- title
- summary
- body
- category
- importance
- audience
- status
- published timestamp
- optional deployment label, build version, or commit range
Categories:
- `Feature`
- `Improvement`
- `Fix`
- `BreakingChange`
Importance:
- `Normal`
- `Important`
Audiences:
- `Everyone`
- `OrganizationOwners`
- `Developers`
Statuses:
- `Draft`
- `Published`
- `Archived`
Only `Published` entries appear to normal users.
### Release Commit
A release commit is a developer-facing record of a Git commit that may need to be matched to a release update.
Fields:
- commit SHA
- short SHA
- subject
- author name/email
- authored timestamp
- committed timestamp
- source branch or deployment label when available
- optional pull request or external URL when available
- communication status
- optional linked release update id
Communication statuses:
- `Unreviewed`
- `Linked`
- `InternalOnly`
- `Ignored`
`Linked` commits are attached to one release update.
`InternalOnly` commits represent real shipped work that should not be visible to users, such as refactors, dependency updates, infrastructure maintenance, or test-only work.
`Ignored` is reserved for commits that should be excluded from the reconciliation view, such as merge noise or accidental imports.
### Read State
Read state is tracked per user and release update.
The system should not rely only on the user's last login timestamp. Login is one opportunity to surface unread updates, but the durable behavior is: a user has unread published release updates until those updates are marked read.
## Frontend Areas
- Whats New badge or entry in the authenticated app shell
- `/app/updates`
- Optional login-time Whats New panel for unread important/recent entries
- Developer-only release communication back office:
- `/app/developer/updates`
- `/app/developer/updates/:id`
- `/app/developer/release-commits`
Feature-owned frontend code belongs under:
```txt
frontend/src/features/release-communications/
```
## Backend Module
Backend feature code should live under:
```txt
backend/src/Socialize.Api/Modules/ReleaseCommunications/
```
Release communications are global SaaS operator data. They are not workspace-owned workflow data.
## Access Rules
- Authenticated users can list and read published release updates visible to their audience.
- Only users with the `Developer` role can create, edit, publish, archive, or delete draft release updates.
- Only users with the `Developer` role can view release commits and commit communication status.
- Developer-only entries are visible only to users with the `Developer` role.
- Organization-owner entries are visible only to users who are owners of at least one organization.
- Archived published entries remain visible in the full update history unless a future retention task changes that behavior.
## User-Facing Behavior
- The app shell shows an unread count for visible published release updates the current user has not read.
- `/app/updates` lists visible published updates sorted by newest first.
- Users can mark one update as read.
- Users can mark all visible updates as read.
- Opening an update from the unread surface should mark that update as read.
- Login may show a non-blocking Whats New panel when unread updates exist.
- Important unread updates should be easier to notice than normal unread updates.
- The update feed should not expose raw commit subjects, commit SHAs, branch names, or internal-only work.
## Developer Back Office
Developers need a reconciliation workflow that connects the real shipped commit history to curated communication entries.
The back office should support:
- create draft update entries
- edit draft entries
- publish entries
- archive published entries
- list imported commits
- filter commits by communication status
- search commits by subject, SHA, author, or linked update
- link one or more commits to an update entry
- unlink a commit from an update entry
- mark commits as internal-only
- mark commits as ignored
- show an "unreviewed commits" count
- show linked commits on update detail pages
The first implementation can import commits through an explicit submitted payload. Repository-backed import must use configured repository connection settings; the application must not assume the deployed filesystem contains a `.git` directory.
## Commit Import Rules
- Commit import should be idempotent by commit SHA.
- Imported commits should not create user-facing update entries automatically.
- Commit subjects should remain visible only in the developer back office.
- A commit can be linked to at most one release update in v1.
- A release update can link many commits.
- Imported commits default to `Unreviewed`.
- Merge commits may be imported but can be marked `Ignored`.
- Commit import should support a bounded range, such as `sinceSha..untilSha` or `sinceDate..untilDate`.
- Repository URL and access credentials belong in configuration/secrets, not hard-coded docs or code.
## Email Digest
Email is useful during alpha/beta but should be digest-based and rate-limited.
Initial email behavior:
- disabled unless explicitly enabled by configuration
- sends at most once per user per day
- sends only when the user has unread visible published updates
- sends only when the user has not logged in or opened the app in at least 24 hours
- initially targets organization owners
- includes concise summaries and a link back to the app
Developer-initiated push email behavior:
- available only to users with the `Developer` role
- sends from a published release update
- uses the release update audience to determine eligible recipients
- supports a confirmation step before sending
- supports an optional "send to me only" test mode
- records when the update was manually emailed, who sent it, and how many recipients were queued or sent
- should prevent accidental duplicate sends for the same update unless the developer explicitly confirms a resend
- should use the same concise email template as digest emails, focused on the selected update
Email delivery should use the existing email infrastructure. Do not introduce a new provider.
Email preferences are out of scope for the first email task. A later task can add user or organization notification preferences.
## API Expectations
Initial user-facing API:
```txt
GET /api/release-updates
GET /api/release-updates/unread
POST /api/release-updates/{id}/read
POST /api/release-updates/read-all
```
Initial developer API:
```txt
GET /api/developer/release-updates
POST /api/developer/release-updates
GET /api/developer/release-updates/{id}
PUT /api/developer/release-updates/{id}
POST /api/developer/release-updates/{id}/publish
POST /api/developer/release-updates/{id}/archive
POST /api/developer/release-updates/{id}/send-email
GET /api/developer/release-commits
POST /api/developer/release-commits/import
POST /api/developer/release-commits/{sha}/link
POST /api/developer/release-commits/{sha}/unlink
POST /api/developer/release-commits/{sha}/internal-only
POST /api/developer/release-commits/{sha}/ignore
```
Backend contract changes require OpenAPI regeneration while the backend is running.
## Localization
User-facing release communication UI must be available in English and French.
Developer-only back-office labels should also be localized when they appear in the app shell.
## Out Of Scope For V1
- Fully automatic update generation from Git commits
- Reading commits from the deployed app filesystem
- Public release notes pages
- Per-user or per-organization email preferences
- Scheduled publishing
- Targeting individual users or individual organizations
- Rich content blocks beyond plain text or basic markdown-style text
- Attachments or screenshots
- Multiple release streams
- Requiring semantic version numbers
- Linking one commit to multiple update entries
## Done When
- [ ] Developers can create and publish curated release updates.
- [ ] Authenticated users can see visible published updates.
- [ ] Users can tell which updates are unread.
- [ ] Users can mark updates read individually and in bulk.
- [ ] The app shell surfaces unread update counts.
- [ ] Developers can import commits into a reconciliation list.
- [ ] Developers can link commits to update entries.
- [ ] Developers can mark commits internal-only or ignored.
- [ ] User-facing update views do not expose internal commit details.
- [ ] Optional email digest and manual push-email behavior are documented and implemented in a separate task.
- [ ] Backend build and tests pass.
- [ ] Frontend build passes.
- [ ] OpenAPI is updated after backend contracts are implemented.

View File

@@ -0,0 +1,71 @@
# Task: Backend release update foundation
## Goal
Add the backend foundation for curated release update entries and per-user read state.
## Feature Spec
- `docs/FEATURES/release-communications.md`
## Scope
- Add a new FastEndpoints module under `backend/src/Socialize.Api/Modules/ReleaseCommunications`.
- Add release update data entities and EF Core model configuration.
- Add per-user release update read receipts.
- Add enum/value support for:
- category: `Feature`, `Improvement`, `Fix`, `BreakingChange`
- importance: `Normal`, `Important`
- audience: `Everyone`, `OrganizationOwners`, `Developers`
- status: `Draft`, `Published`, `Archived`
- Add `DbSet` entries and module configuration to `AppDbContext`.
- Add current-user API endpoints:
- list visible published release updates
- get unread visible release updates
- mark one release update as read
- mark all visible release updates as read
- Add developer API endpoints:
- list all release updates
- create draft release update
- get release update detail
- update draft release update
- publish release update
- archive release update
- Enforce access rules:
- authenticated users can read only visible published updates
- only `Developer` users can manage update entries
- organization-owner audience only appears to users who own at least one organization
- developer audience only appears to `Developer` users
- Keep commit import and email digest out of this task.
## Likely Files
- `backend/src/Socialize.Api/Data/AppDbContext.cs`
- `backend/src/Socialize.Api/Modules/ReleaseCommunications/**`
- `backend/tests/Socialize.Tests/**`
## Notes
- Treat release communications as global SaaS operator data, not workspace-owned workflow data.
- Use FastEndpoints handlers and keep request/response records near handlers unless local module patterns suggest otherwise.
- Use FluentValidation for non-trivial input.
- Do not expose draft entries to non-developer users.
- Do not expose commit metadata in user-facing DTOs.
## Validation
```bash
dotnet build backend/Socialize.slnx
dotnet test backend/Socialize.slnx
```
## Done When
- [x] Developers can create draft release update entries.
- [x] Developers can publish and archive release updates.
- [x] Authenticated users can list visible published updates.
- [x] Audience filtering is enforced.
- [x] Users can mark one update read.
- [x] Users can mark all visible updates read.
- [x] Unread queries only count visible published updates.
- [x] Backend tests cover access rules and read state.

View File

@@ -0,0 +1,54 @@
# Task: Frontend Whats New experience
## Goal
Add the user-facing Whats New experience for published release updates and unread state.
## Feature Spec
- `docs/FEATURES/release-communications.md`
## Scope
- Add feature-owned frontend code under `frontend/src/features/release-communications/`.
- Add `/app/updates`.
- Add an app shell entry or badge for unread release updates.
- Fetch visible published release updates from the backend.
- Show unread state for update entries.
- Mark an update as read when opened.
- Add a mark-all-read action.
- Optionally show a non-blocking login-time Whats New panel when unread updates exist.
- Add English and French locale strings.
- Keep developer authoring UI, commit reconciliation, and email digest out of this task.
## Likely Files
- `frontend/src/router/router.js`
- `frontend/src/layouts/main/**`
- `frontend/src/features/release-communications/**`
- `frontend/src/locales/en.json`
- `frontend/src/locales/fr.json`
## Notes
- The user-facing update feed must be curated and should not show raw commit SHAs, commit subjects, branch names, or internal-only work.
- Keep the UI compact and app-like. This is an operational app surface, not a marketing release notes page.
- Use the shared Axios API client in `frontend/src/plugins/api.js`.
## Validation
```bash
cd frontend
npm run build
```
## Done When
- [x] Authenticated users can open `/app/updates`.
- [x] The app shell shows unread update count.
- [x] Published visible updates are listed newest first.
- [x] Unread updates are visually distinct.
- [x] Opening an update marks it read.
- [x] Users can mark all visible updates read.
- [x] UI strings exist in English and French.
- [x] Frontend build passes.

View File

@@ -0,0 +1,80 @@
# Task: Developer commit reconciliation
## Goal
Add the developer back-office workflow for importing shipped commits and matching them to curated release update entries.
## Feature Spec
- `docs/FEATURES/release-communications.md`
## Scope
- Add release commit persistence and EF Core model configuration.
- Add enum/value support for communication status:
- `Unreviewed`
- `Linked`
- `InternalOnly`
- `Ignored`
- Add developer API endpoints:
- list imported commits
- import commits for a bounded range
- link a commit to a release update
- unlink a commit from a release update
- mark a commit internal-only
- mark a commit ignored
- Add developer-only frontend screens:
- `/app/developer/release-commits`
- linked commits on `/app/developer/updates/:id`
- Support filters for:
- communication status
- linked update
- author
- date range
- text search by subject or SHA
- Show an unreviewed commit count.
- Keep user-facing update views free of commit metadata.
- Keep automatic CI deployment integration out of this task.
## Likely Files
- `backend/src/Socialize.Api/Data/AppDbContext.cs`
- `backend/src/Socialize.Api/Modules/ReleaseCommunications/**`
- `frontend/src/router/router.js`
- `frontend/src/layouts/main/**`
- `frontend/src/features/release-communications/**`
- `frontend/src/locales/en.json`
- `frontend/src/locales/fr.json`
- `backend/tests/Socialize.Tests/**`
## Notes
- Commit import must be idempotent by SHA.
- A commit can be linked to at most one release update in v1.
- A release update can have many linked commits.
- Imported commits default to `Unreviewed`.
- Import must use either a submitted commit payload or configured repository connection settings. Do not discover or read a local `.git` directory from the deployed app filesystem.
- Repository URL and access credentials must come from configuration/secrets.
- Do not generate user-facing update entries automatically from commits.
## Validation
```bash
dotnet build backend/Socialize.slnx
dotnet test backend/Socialize.slnx
cd frontend
npm run build
```
## Done When
- [x] Developers can import commits idempotently.
- [x] Developers can list and filter imported commits.
- [x] Developers can link commits to release updates.
- [x] Developers can unlink commits.
- [x] Developers can mark commits internal-only.
- [x] Developers can mark commits ignored.
- [x] Release update detail shows linked commits to developers.
- [x] Unreviewed commit count is visible to developers.
- [x] Non-developer users cannot access commit reconciliation APIs or UI.
- [x] User-facing update views do not expose commit metadata.

View File

@@ -0,0 +1,70 @@
# Task: Release update email digest
## Goal
Add optional daily email digests for inactive users with unread release updates, plus a developer-operated manual email push for important published updates.
## Feature Spec
- `docs/FEATURES/release-communications.md`
## Scope
- Add configuration to enable or disable release update email digests.
- Add persistence needed to rate-limit digest sends per user.
- Send at most one digest per user per day.
- Send only when the user has unread visible published release updates.
- Send only when the user has not logged in or opened the app in at least 24 hours.
- Initially target organization owners.
- Use the existing email infrastructure.
- Add a developer-only API endpoint to send an email announcement for a selected published release update.
- Add a developer-only back-office button for sending the selected update by email.
- Require confirmation before sending a manual push email.
- Support a "send to me only" test mode.
- Record manual push email metadata:
- sent by user id
- sent timestamp
- selected audience
- recipient count
- Prevent accidental duplicate push sends unless the developer explicitly confirms a resend.
- Keep user or organization email preferences out of this task.
## Likely Files
- `backend/src/Socialize.Api/Infrastructure/Emailer/**`
- `backend/src/Socialize.Api/Modules/ReleaseCommunications/**`
- `backend/src/Socialize.Api/Modules/Identity/**`
- `backend/src/Socialize.Api/Modules/Organizations/**`
- `frontend/src/features/release-communications/**`
- `frontend/src/locales/en.json`
- `frontend/src/locales/fr.json`
- `backend/tests/Socialize.Tests/**`
## Notes
- This task may require tracking a user's last app activity timestamp if login timestamp alone is not enough.
- Keep email copy concise and product-specific.
- The digest should link back to the app's Whats New page.
- Manual push emails should link directly to the selected update when possible.
- Do not introduce a new email provider.
## Validation
```bash
dotnet build backend/Socialize.slnx
dotnet test backend/Socialize.slnx
```
## Done When
- [x] Digest delivery is disabled unless explicitly configured.
- [x] Eligible organization owners receive at most one digest per day.
- [x] Digests are sent only when unread visible release updates exist.
- [x] Active users are not emailed.
- [x] Developers can manually send a published update email from the back office.
- [x] Developers can send a manual update email to themselves as a test.
- [x] Manual push sends require confirmation.
- [x] Manual push sends record sender, timestamp, audience, and recipient count.
- [x] Duplicate manual sends require explicit resend confirmation.
- [x] Email delivery uses existing email infrastructure.
- [x] Backend tests cover eligibility and rate limiting.