Introduces the SDD root: a v1.0.0 constitution and machine-readable AGENTS.md grounded in the project's real conventions; six EARS-aware persona spec-review checklists that cross-reference .claude/personas/; feature-spec/ADR/threat-model/ api-contract templates; a fully worked _example feature; a living RTM; and an adrs/ pointer that reuses the existing docs/adr/ archive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
77 lines
3.9 KiB
Markdown
77 lines
3.9 KiB
Markdown
# Persona Review Results — Profile picture upload
|
|
|
|
> Captured from the six persona spec reviews (the comments that, in a real feature, are
|
|
> posted on the Gitea issue). This is the worked example of what a completed review round
|
|
> looks like. All personas APPROVE; the two findings raised were folded into the spec
|
|
> before approval.
|
|
|
|
## Summary
|
|
|
|
| Persona | Verdict | Blocking FAILs | Notes |
|
|
|---|---|---|---|
|
|
| Requirements Engineer | APPROVE | none | — |
|
|
| Developer | APPROVE | none | — |
|
|
| Security | APPROVE | none (2 resolved) | See F-SEC-1, F-SEC-2 |
|
|
| DevOps | APPROVE | none | — |
|
|
| UI/UX | APPROVE | none (1 resolved) | See F-UX-1 |
|
|
| Architect | APPROVE | none (1 resolved) | See F-ARCH-1 |
|
|
|
|
---
|
|
|
|
## ### Security — Spec Review
|
|
|
|
| # | Item | Status | Note |
|
|
|---|---|---|---|
|
|
| 1 | All mutating endpoints have authn + authz `If` clauses | PASS | REQ-006 (401), REQ-009 (403) |
|
|
| 2 | Each mutating endpoint names least-privilege `Permission` | PASS | `me` = authenticated; `{id}` = ADMIN_USER |
|
|
| 3 | Audit fields server-set, forbidden in body | PASS | `avatarObjectKey` server-set (design.md) |
|
|
| 4 | IDOR surfaces addressed | PASS | `/{id}` gated by ADMIN_USER + ownership |
|
|
| 5 | Untrusted content rendered safely | PASS | image bytes via proxy + `nosniff` |
|
|
| 6 | Upload: type allow-list + size + bytes | PASS | REQ-007 (PNG/JPEG), REQ-008 (2 MB) |
|
|
| 7 | No entity internals leaked | PASS | `UserProfileView`, not `AppUser` |
|
|
| 8 | Conflicts → 409 not raw 500 | N/A | no optimistic-lock surface here |
|
|
| 9 | threat-model.md present & STRIDE-complete | PASS | [threat-model.md](./threat-model.md) |
|
|
| 10 | ASTRIDE if AI tool used | N/A | no AI agent |
|
|
| 11 | Secrets from env only | PASS | none introduced |
|
|
| 12 | Logs PII-free | PASS | user UUID only |
|
|
| 13 | New dependency has ADR + clean audit | N/A | no new dependency |
|
|
|
|
**F-SEC-1 (resolved):** initial draft exposed a public S3 URL for `avatarUrl` →
|
|
information disclosure. Resolved: authenticated proxy `GET /api/users/{id}/avatar`.
|
|
**F-SEC-2 (resolved):** initial draft bound `avatarObjectKey` from the request body →
|
|
mass-assignment. Resolved: server-set only.
|
|
**Verdict: APPROVE.**
|
|
|
|
## ### UI/UX — Spec Review
|
|
|
|
| # | Item | Status | Note |
|
|
|---|---|---|---|
|
|
| 1 | Every interaction state described | PASS | idle/preview/uploading/error/done (T-10) |
|
|
| 2 | Strings via Paraglide i18n | PASS | T-8 |
|
|
| 3 | Reuses design tokens/components | PASS | placeholder uses existing initials pattern |
|
|
| 4 | Responsive per device split | PASS | control usable on phone + laptop |
|
|
| 5 | Errors via `getErrorMessage(code)` | PASS | UNSUPPORTED_FILE_TYPE / AVATAR_TOO_LARGE |
|
|
| 6 | Keyboard + screen-reader | PASS | labelled file input, alt text on image |
|
|
| 7 | Acceptance criteria measurable | PASS | sizes, status codes |
|
|
| 8 | E2E scenario per journey | PASS | T-12 |
|
|
| 9 | Confirmation for destructive action | PASS | remove asks to confirm |
|
|
| 10 | Safe rendering + image dims | PASS | fixed dims avoid layout shift |
|
|
| 11 | Live routes verified | PASS | `/profile`, `/users/[id]` exist |
|
|
| 12 | Token theming respected | PASS | semantic tokens |
|
|
|
|
**F-UX-1 (resolved):** no loading state in first draft → spinner during upload added (REQ-... covered by state set in T-10).
|
|
**Verdict: APPROVE.**
|
|
|
|
## ### Architect — Spec Review
|
|
|
|
Key items PASS. **F-ARCH-1 (resolved):** bucket choice was undocumented → captured in
|
|
[adr-001-avatars-reuse-archive-bucket.md](./adr-001-avatars-reuse-archive-bucket.md). No new
|
|
domain, no boundary crossing, Person/AppUser separation intact. **Verdict: APPROVE.**
|
|
|
|
## ### Requirements Engineer / Developer / DevOps — Spec Review
|
|
|
|
All checklist items PASS (see each persona's checklist in `.specify/personas/`). RE: 9 REQ
|
|
ids, all EARS-formed, every limit has an `If`. Developer: reuses `FileService`/`UserService`,
|
|
`AVATAR_TOO_LARGE` four-site update is T-1. DevOps: V78 forward-only + rollback note, no new
|
|
bucket/env var, idempotent overwrite. **All three: APPROVE.**
|