# Requirements Traceability Matrix (RTM) > Living document. One row per `REQ-NNN` across all in-flight and shipped features. It links > a requirement to the design that realises it, the code that implements it, and the > test(s) that prove it — so any requirement can be traced end to end, and any orphan > (a requirement with no test, or a test with no requirement) is visible. ## How to update 1. When a `spec.md` is approved, copy its `## Traceability` rows here with `Status: Planned`. 2. As tasks land, fill `Implementation File(s)` and flip `Status` → `In progress` → `Done`. 3. `REQ-ID`s are **scoped per feature**, so always qualify with the Feature column — `REQ-001` in *avatar* is not `REQ-001` in another feature. 4. The `sdd-gate.yml` CI job (`traceability-check`) warns (non-blocking, for now) when a `spec.md` contains a `REQ-NNN` that does not appear in this file. Keep it in sync to keep the warning quiet; it flips to blocking once adoption settles (see the workflow's TODO). ## Status legend `Planned` · `In progress` · `Done` · `Deferred` ## Matrix | REQ-ID | Requirement Summary | Feature | Design Artifact | Implementation File(s) | Test(s) | Status | |---|---|---|---|---|---|---| | REQ-001 | Store avatar at `avatars/{userId}`, overwrite | profile-picture-upload (_example) | features/_example/design.md; adr-001 | `UserService` (planned) | `UserServiceAvatarTest#storesUnderUserKey`, `#replaceLeavesNoOrphan` | Planned | | REQ-002 | Upload self avatar → 200 + avatarUrl | profile-picture-upload (_example) | features/_example/design.md; api-contract.yaml | `UserAvatarController`, `UserService` (planned) | `UserAvatarControllerTest#uploadReturnsAvatarUrl` | Planned | | REQ-003 | Delete self avatar → avatarUrl null | profile-picture-upload (_example) | features/_example/api-contract.yaml | `UserAvatarController` (planned) | `UserAvatarControllerTest#deleteClearsAvatar` | Planned | | REQ-004 | No avatar → null + initials placeholder | profile-picture-upload (_example) | features/_example/design.md | `UserProfileView`, avatar component (planned) | `avatar-placeholder.svelte.spec.ts` | Planned | | REQ-005 | ADMIN_USER may delete others' avatar | profile-picture-upload (_example) | features/_example/api-contract.yaml | `UserAvatarController` (planned) | `UserAvatarControllerTest#adminDeletesOthersAvatar` | Planned | | REQ-006 | Unauthenticated → 401, store nothing | profile-picture-upload (_example) | features/_example/threat-model.md | `SecurityConfig`, controller (planned) | `UserAvatarControllerTest#unauthenticatedReturns401` | Planned | | REQ-007 | Non-image → 400 UNSUPPORTED_FILE_TYPE | profile-picture-upload (_example) | features/_example/design.md | `UserService` (planned) | `UserAvatarControllerTest#rejectsNonImage` | Planned | | REQ-008 | Over 2 MB → 400 AVATAR_TOO_LARGE | profile-picture-upload (_example) | features/_example/design.md | `UserService`, `ErrorCode` (planned) | `UserAvatarControllerTest#rejectsOversize` | Planned | | REQ-009 | Non-admin on others → 403 FORBIDDEN | profile-picture-upload (_example) | features/_example/threat-model.md | `UserAvatarController` (planned) | `UserAvatarControllerTest#nonAdminForbiddenOnOthers` | Planned |