The Gitea issue body is the single source of truth for a spec; the only per-feature artifact in git is the RTM row (REQ-ID -> issue # -> test). Drops per-feature spec.md/tasks.md/checklist files from the workflow (the _example stays as a template/reference). Updates the guide, ADR-041, AGENTS.md, CLAUDE.md, templates, the RTM (adds an Issue column), the implement/review-pr skills, and replaces the file-spec CI jobs with an rtm-check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
3.3 KiB
Markdown
40 lines
3.3 KiB
Markdown
# Requirements Traceability Matrix (RTM)
|
|
|
|
> Living document. One row per `REQ-NNN` across all in-flight and shipped features. The spec
|
|
> itself lives in the **Gitea issue** (issue-only — there is no committed `spec.md`); this
|
|
> matrix is the part of the spec that *is* committed: it links each requirement to its issue,
|
|
> the code that implements it, and the test(s) that prove it — so any requirement traces end
|
|
> to end, and any orphan (a requirement with no test) is visible on `main`.
|
|
|
|
## How to update
|
|
|
|
1. When a feature's issue is approved (via `/review-issue`), add one row per `REQ-NNN` with the
|
|
`Issue` set to the Gitea issue number and `Status: Planned`. Commit these rows on the feature
|
|
branch (they merge with the feature's PR).
|
|
2. As tasks land, fill `Implementation File(s)` + `Test(s)` and flip `Status` →
|
|
`In progress` → `Done`.
|
|
3. `REQ-ID`s are **scoped per feature**, so always read them together with the `Issue` column —
|
|
`REQ-001` for issue #142 is not `REQ-001` for issue #150.
|
|
4. The `sdd-gate.yml` CI job (`rtm-check`) warns (non-blocking, for now) when a row is missing
|
|
its `Issue` or `Test(s)`. It flips to blocking once adoption settles (see the workflow's TODO).
|
|
|
|
## Status legend
|
|
|
|
`Planned` · `In progress` · `Done` · `Deferred`
|
|
|
|
## Matrix
|
|
|
|
| REQ-ID | Requirement Summary | Issue | Feature | Implementation File(s) | Test(s) | Status |
|
|
|---|---|---|---|---|---|---|
|
|
| REQ-001 | Store avatar at `avatars/{userId}`, overwrite | #example | profile-picture-upload (_example) | `UserService` (planned) | `UserServiceAvatarTest#storesUnderUserKey`, `#replaceLeavesNoOrphan` | Planned |
|
|
| REQ-002 | Upload self avatar → 200 + avatarUrl | #example | profile-picture-upload (_example) | `UserAvatarController`, `UserService` (planned) | `UserAvatarControllerTest#uploadReturnsAvatarUrl` | Planned |
|
|
| REQ-003 | Delete self avatar → avatarUrl null | #example | profile-picture-upload (_example) | `UserAvatarController` (planned) | `UserAvatarControllerTest#deleteClearsAvatar` | Planned |
|
|
| REQ-004 | No avatar → null + initials placeholder | #example | profile-picture-upload (_example) | `UserProfileView`, avatar component (planned) | `avatar-placeholder.svelte.spec.ts` | Planned |
|
|
| REQ-005 | ADMIN_USER may delete others' avatar | #example | profile-picture-upload (_example) | `UserAvatarController` (planned) | `UserAvatarControllerTest#adminDeletesOthersAvatar` | Planned |
|
|
| REQ-006 | Unauthenticated → 401, store nothing | #example | profile-picture-upload (_example) | `SecurityConfig`, controller (planned) | `UserAvatarControllerTest#unauthenticatedReturns401` | Planned |
|
|
| REQ-007 | Non-image → 400 UNSUPPORTED_FILE_TYPE | #example | profile-picture-upload (_example) | `UserService` (planned) | `UserAvatarControllerTest#rejectsNonImage` | Planned |
|
|
| REQ-008 | Over 2 MB → 400 AVATAR_TOO_LARGE | #example | profile-picture-upload (_example) | `UserService`, `ErrorCode` (planned) | `UserAvatarControllerTest#rejectsOversize` | Planned |
|
|
| REQ-009 | Non-admin on others → 403 FORBIDDEN | #example | profile-picture-upload (_example) | `UserAvatarController` (planned) | `UserAvatarControllerTest#nonAdminForbiddenOnOthers` | Planned |
|
|
|
|
<!-- Append real features below this line, one row per REQ-NNN, with the real issue number. Keep the header row above. -->
|