feat: add release communications
This commit is contained in:
280
docs/FEATURES/release-communications.md
Normal file
280
docs/FEATURES/release-communications.md
Normal 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
|
||||
|
||||
- What’s New badge or entry in the authenticated app shell
|
||||
- `/app/updates`
|
||||
- Optional login-time What’s 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 What’s 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.
|
||||
@@ -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.
|
||||
54
docs/TASKS/release-communications/002-frontend-whats-new.md
Normal file
54
docs/TASKS/release-communications/002-frontend-whats-new.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Task: Frontend What’s New experience
|
||||
|
||||
## Goal
|
||||
|
||||
Add the user-facing What’s 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 What’s 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.
|
||||
@@ -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.
|
||||
70
docs/TASKS/release-communications/004-email-digest.md
Normal file
70
docs/TASKS/release-communications/004-email-digest.md
Normal 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 What’s 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.
|
||||
Reference in New Issue
Block a user