docs(05-01): drop 27-migrations target and move HTTP fuzz to Phase 12

- DATA-09 and Phase 5 criteria follow D-01 squash and D-07 service-level fuzz
- Phase 12 inherits the write-endpoint DTO fuzz once HTTP exists
This commit is contained in:
Jakub Zych
2026-09-18 18:48:08 +02:00
parent 52638fb4e3
commit c85fa24936
2 changed files with 6 additions and 5 deletions

View File

@@ -44,7 +44,7 @@ Requirements for v1 (the Płytarium port). Each maps to roadmap phases. "User" b
- [ ] **DATA-06**: Mass assignment goes through per-endpoint request DTOs honoring each model's fillable allow-list; serialization honors a hidden deny-list with an explicit per-call override - [ ] **DATA-06**: Mass assignment goes through per-endpoint request DTOs honoring each model's fillable allow-list; serialization honors a hidden deny-list with an explicit per-call override
- [ ] **DATA-07**: Custom casts exist for jsonable columns, money as a fixed four-decimal string, and encrypted-at-rest secrets (AES-GCM, app-key derived) that are also hidden from serialization - [ ] **DATA-07**: Custom casts exist for jsonable columns, money as a fixed four-decimal string, and encrypted-at-rest secrets (AES-GCM, app-key derived) that are also hidden from serialization
- [ ] **DATA-08**: A polymorphic file attachment table (owner type, owner id, field, disk path, sort order, public/private) backs attachOne and attachMany, stored via gocloud.dev/blob with the same public URL shape - [ ] **DATA-08**: A polymorphic file attachment table (owner type, owner id, field, disk path, sort order, public/private) backs attachOne and attachMany, stored via gocloud.dev/blob with the same public URL shape
- [ ] **DATA-09**: All 25 Płytarium models and 27 migrations are ported with matching table names, columns, indexes and defaults - [ ] **DATA-09**: All 25 Płytarium models and their squashed migration set are ported with matching table names, columns, indexes and defaults (migration count is not itself an acceptance number — squashed per plan-time decision D-01 in 05-CONTEXT.md)
- [ ] **DATA-10**: Paginated responses use the exact `{data, meta{current_page, last_page, per_page, total}}` envelope without a links key - [ ] **DATA-10**: Paginated responses use the exact `{data, meta{current_page, last_page, per_page, total}}` envelope without a links key
- [ ] **DATA-11**: Other plugins can hook a model's lifecycle through the GORM callback registry and extend its schema with a companion migration - [ ] **DATA-11**: Other plugins can hook a model's lifecycle through the GORM callback registry and extend its schema with a companion migration

View File

@@ -17,7 +17,7 @@ Decimal phases appear between their surrounding integers in numeric order.
- [x] **Phase 2: API parity harness bootstrap** - Fixture recorder + replay-and-diff harness against the live PHP backend, built on the Phase 1 command kernel (completed 2026-09-17) - [x] **Phase 2: API parity harness bootstrap** - Fixture recorder + replay-and-diff harness against the live PHP backend, built on the Phase 1 command kernel (completed 2026-09-17)
- [x] **Phase 3: First vertical slice — genres end to end** - `GET /_fonoteka/api/v1/genres` passes the parity diff through every layer (completed 2026-09-17) - [x] **Phase 3: First vertical slice — genres end to end** - `GET /_fonoteka/api/v1/genres` passes the parity diff through every layer (completed 2026-09-17)
- [x] **Phase 4: CLI scaffolding, i18n and mail** - Scaffolding commands, translated/pluralized strings, mail templates (completed 2026-09-18) - [x] **Phase 4: CLI scaffolding, i18n and mail** - Scaffolding commands, translated/pluralized strings, mail templates (completed 2026-09-18)
- [ ] **Phase 5: Data layer full fidelity** - All 25 models and 27 migrations with fillable/hidden/cast/soft-delete discipline - [ ] **Phase 5: Data layer full fidelity** - All 25 models and their squashed migrations with fillable/hidden/cast/soft-delete discipline
- [ ] **Phase 6: HTTP routing, auth groups and rate limiting** - Three auth groups, named rate buckets, OAuth-safe middleware structure - [ ] **Phase 6: HTTP routing, auth groups and rate limiting** - Three auth groups, named rate buckets, OAuth-safe middleware structure
- [ ] **Phase 7: User plugin and authentication** - Registration, login, JWT, organizations, personal tokens, must-change-password - [ ] **Phase 7: User plugin and authentication** - Registration, login, JWT, organizations, personal tokens, must-change-password
- [ ] **Phase 8: OAuth2.1 authorization server** - zitadel/oidc server for fonoteka-mcp and the ChatGPT connector - [ ] **Phase 8: OAuth2.1 authorization server** - zitadel/oidc server for fonoteka-mcp and the ChatGPT connector
@@ -171,16 +171,16 @@ Plans:
### Phase 5: Data layer full fidelity ### Phase 5: Data layer full fidelity
**Goal**: All 25 Płytarium models and 27 migrations are ported with matching relations, casts, hooks, and the fillable/hidden/encrypted-cast mass-assignment and serialization discipline. This is security-load-bearing — mass-assignment boundaries and encrypted-at-rest credential casts are named security invariants in the PHP source — apply the security-review agent and the DTO-vs-model convention from the first model onward. **Goal**: All 25 Płytarium models and their squashed migration set are ported with matching relations, casts, hooks, and the fillable/hidden/encrypted-cast mass-assignment and serialization discipline. This is security-load-bearing — mass-assignment boundaries and encrypted-at-rest credential casts are named security invariants in the PHP source — apply the security-review agent and the DTO-vs-model convention from the first model onward.
**Mode:** mvp **Mode:** mvp
**Depends on**: Phase 1, Phase 3 **Depends on**: Phase 1, Phase 3
**Repos:** summercms.go, fonoteka.go **Repos:** summercms.go, fonoteka.go
**Requirements**: DATA-03, DATA-04, DATA-05, DATA-06, DATA-07, DATA-08, DATA-09, DATA-10, DATA-11, CLI-03 **Requirements**: DATA-03, DATA-04, DATA-05, DATA-06, DATA-07, DATA-08, DATA-09, DATA-10, DATA-11, CLI-03
**Success Criteria** (what must be TRUE): **Success Criteria** (what must be TRUE):
1. All 25 models exist with matching table names, columns, indexes and defaults; all 27 migrations run up and down individually, and `summer migrate:rollback --plugin=fonoteka` rolls back only that plugin's last migration. 1. All 25 models exist with matching table names, columns, indexes and defaults, and every Go migration runs up and down individually; the final schema matches PHP's (migrations are squashed per final-state table, not a 1:1 port of PHP's 38 files — the historical migration count is not a target). `summer migrate:rollback --plugin=fonoteka` rolls back only that plugin's last migration.
2. A many-to-many relation with pivot business columns (`album_artists.sort_order`, `CollectionEditor.role/granted_at/granted_by`) round-trips correctly for a 3+ artist album; belongsTo/hasOne/hasMany relations return ordered results. 2. A many-to-many relation with pivot business columns (`album_artists.sort_order`, `CollectionEditor.role/granted_at/granted_by`) round-trips correctly for a 3+ artist album; belongsTo/hasOne/hasMany relations return ordered results.
3. Every write endpoint uses a request DTO that enforces its model's fillable allow-list (a fuzz test posting unknown fields asserts they are rejected or ignored, never persisted), and serialization honors the hidden deny-list with an explicit per-call override. 3. The fill boundary of the PHP write services (at minimum Album, Collection and the four credential models) is fuzzed against real Postgres with random extra and server-owned keys, asserting nothing outside the allow-list is persisted (service-level, this phase); a request-DTO-level fuzz over every write endpoint is Phase 12's criterion (the HTTP layer does not exist until Phase 6/12). Serialization honors the hidden deny-list with an explicit per-call override.
4. The money cast round-trips the PHP ceiling and blank-string cases as a fixed 4-decimal JSON string (never `float64`), and an encrypted-at-rest credential column is AES-GCM encrypted at rest and hidden from serialization. 4. The money cast round-trips the PHP ceiling and blank-string cases as a fixed 4-decimal JSON string (never `float64`), and an encrypted-at-rest credential column is AES-GCM encrypted at rest and hidden from serialization.
5. Paginated responses use the exact `{data, meta{current_page,last_page,per_page,total}}` envelope with no `links` key; another plugin extends a model's lifecycle through the GORM callback registry and a companion migration without editing the owning plugin's file; soft-deletable + uniquely-keyed tables pass a delete-then-recreate test. 5. Paginated responses use the exact `{data, meta{current_page,last_page,per_page,total}}` envelope with no `links` key; another plugin extends a model's lifecycle through the GORM callback registry and a companion migration without editing the owning plugin's file; soft-deletable + uniquely-keyed tables pass a delete-then-recreate test.
@@ -324,6 +324,7 @@ Plans:
2. Albums CRUD, ratings, reservations, photo upload, manual cover URL and Discogs cover price all pass the parity diff. 2. Albums CRUD, ratings, reservations, photo upload, manual cover URL and Discogs cover price all pass the parity diff.
3. Album search treats Typesense results as a pre-filter re-gated in SQL, verified by a security test that a stale/mis-scoped search document cannot leak an unauthorized result. 3. Album search treats Typesense results as a pre-filter re-gated in SQL, verified by a security test that a stale/mis-scoped search document cannot leak an unauthorized result.
4. Artists/genres/styles lookup endpoints used by the Albums UI pass the parity diff. 4. Artists/genres/styles lookup endpoints used by the Albums UI pass the parity diff.
5. A request-DTO-level fuzz over every write endpoint asserts unknown and server-owned keys are never persisted (inherits the HTTP half of Phase 5 criterion 3; the HTTP layer does not exist until Phase 6/12).
**Plans**: TBD **Plans**: TBD