All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m33s
CI / OCR Service Tests (pull_request) Successful in 24s
CI / Backend Unit Tests (pull_request) Successful in 4m37s
CI / fail2ban Regex (pull_request) Successful in 43s
CI / Semgrep Security Scan (pull_request) Successful in 21s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m7s
SDD Gate / RTM Check (pull_request) Successful in 15s
SDD Gate / Contract Validate (pull_request) Successful in 24s
SDD Gate / Constitution Impact (pull_request) Successful in 17s
Flip REQ-001..006 for the timeline date-label feature from Planned to Done and fill the implementing files plus the concrete dateLabel.spec.ts test names that prove each requirement. Closes #778 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.9 KiB
4.9 KiB
Requirements Traceability Matrix (RTM)
Living document. One row per
REQ-NNNacross all in-flight and shipped features. The spec itself lives in the Gitea issue (issue-only — there is no committedspec.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 onmain.
How to update
- When a feature's issue is approved (via
/review-issue), add one row perREQ-NNNwith theIssueset to the Gitea issue number andStatus: Planned. Commit these rows on the feature branch (they merge with the feature's PR). - As tasks land, fill
Implementation File(s)+Test(s)and flipStatus→In progress→Done. REQ-IDs are scoped per feature, so always read them together with theIssuecolumn —REQ-001for issue #142 is notREQ-001for issue #150.- The
sdd-gate.ymlCI job (rtm-check) warns (non-blocking, for now) when a row is missing itsIssueorTest(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 |
| REQ-001 | Render dated entry via shared formatDocumentDate (de/en/es) |
#778 | timeline-date-label | frontend/src/lib/timeline/dateLabel.ts |
dateLabel.spec.ts › renders a DAY date localized in German, renders a SEASON date with the German season word, delegates a same-year RANGE to formatDocumentDate |
Done |
| REQ-002 | Non-UNKNOWN + non-empty date → shared label, raw=null, getLocale() |
#778 | timeline-date-label | frontend/src/lib/timeline/dateLabel.ts |
dateLabel.spec.ts › renders a DAY date localized in German, renders a DAY date localized in English |
Done |
| REQ-003 | UNKNOWN → null (no chip) |
#778 | timeline-date-label | frontend/src/lib/timeline/dateLabel.ts |
dateLabel.spec.ts › returns null for UNKNOWN precision even with a date, returns null for UNKNOWN precision without a date |
Done |
| REQ-004 | null/undefined/'' eventDate → null, no formatter call |
#778 | timeline-date-label | frontend/src/lib/timeline/dateLabel.ts |
dateLabel.spec.ts › returns null for APPROX with a null eventDate, without calling the formatter, returns null for DAY with an empty-string eventDate, treats undefined eventDateEnd identically to null for RANGE |
Done |
| REQ-005 | No rendering logic outside documentDate.ts |
#778 | timeline-date-label | frontend/src/lib/timeline/dateLabel.ts (façade) |
dateLabel.spec.ts › delegates a same-year RANGE to formatDocumentDate (asserts byte-identical delegation) |
Done |
| REQ-006 | timeline in coverage include (80% branch gate) |
#778 | timeline-date-label | frontend/vite.config.ts |
coverage include now lists src/lib/timeline/**; covered by all dateLabel.spec.ts cases |
Done |