docs(09-06): complete albums admin collection boundary plan

This commit is contained in:
Jakub Zych
2026-09-24 20:22:07 +02:00
parent 0caa86ec0b
commit 2a955d6447
3 changed files with 276 additions and 10 deletions

View File

@@ -375,7 +375,7 @@ Plans:
4. The relation-manager schema supports search/link/unlink/manage-or-view lists for Collections' editors tab, replacing the `partial` field entirely. 4. The relation-manager schema supports search/link/unlink/manage-or-view lists for Collections' editors tab, replacing the `partial` field entirely.
5. Admin CRUD endpoints expose `listExtendQuery`/`formExtendQuery`/`formBeforeCreate`/`formBeforeUpdate`/`relationExtendManageQuery` hooks, bulk delete runs each record's lifecycle hooks, and the Settings model binds to a settings screen through the same schema pipeline. 5. Admin CRUD endpoints expose `listExtendQuery`/`formExtendQuery`/`formBeforeCreate`/`formBeforeUpdate`/`relationExtendManageQuery` hooks, bulk delete runs each record's lifecycle hooks, and the Settings model binds to a settings screen through the same schema pipeline.
**Plans**: 5/12 plans executed **Plans**: 6/12 plans executed
**Research flag:** yes **Research flag:** yes
Plans: Plans:
@@ -396,7 +396,7 @@ Plans:
- [x] 09-05-PLAN.md — Deliver schema-projected CRUD and transactional bulk deletion - [x] 09-05-PLAN.md — Deliver schema-projected CRUD and transactional bulk deletion
**Wave 6** *(blocked on Wave 5 completion)* **Wave 6** *(blocked on Wave 5 completion)*
- [ ] 09-06-PLAN.md — Port the Albums admin surface and the collection boundary - [x] 09-06-PLAN.md — Port the Albums admin surface and the collection boundary
**Wave 7** *(blocked on Wave 6 completion)* **Wave 7** *(blocked on Wave 6 completion)*
- [ ] 09-07-PLAN.md — Port the Artists backend controller - [ ] 09-07-PLAN.md — Port the Artists backend controller
@@ -529,7 +529,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
| 6. HTTP routing, auth groups and rate limiting | 14/14 | Complete | 2026-09-21 | | 6. HTTP routing, auth groups and rate limiting | 14/14 | Complete | 2026-09-21 |
| 7. User plugin and authentication | 8/8 | Complete | 2026-09-23 | | 7. User plugin and authentication | 8/8 | Complete | 2026-09-23 |
| 8. OAuth2.1 authorization server | 10/10 | Complete | 2026-09-23 | | 8. OAuth2.1 authorization server | 10/10 | Complete | 2026-09-23 |
| 9. Backend admin authentication and schema pipeline | 5/12 | In Progress| | | 9. Backend admin authentication and schema pipeline | 6/12 | In Progress| |
| 10. Admin Vue SPA | 0/TBD | Not started | - | | 10. Admin Vue SPA | 0/TBD | Not started | - |
| 11. Jobs, realtime and search infrastructure | 0/TBD | Not started | - | | 11. Jobs, realtime and search infrastructure | 0/TBD | Not started | - |
| 12. Płytarium API — Collections and Albums | 0/TBD | Not started | - | | 12. Płytarium API — Collections and Albums | 0/TBD | Not started | - |

View File

@@ -4,16 +4,16 @@ milestone: v1.0
current_phase: 09 current_phase: 09
current_phase_name: Backend admin authentication and schema pipeline current_phase_name: Backend admin authentication and schema pipeline
status: executing status: executing
stopped_at: Completed 09-05-PLAN.md stopped_at: Completed 09-06-PLAN.md
last_updated: "2026-09-24T17:46:54.525Z" last_updated: "2026-09-24T18:21:51.771Z"
last_activity: 2026-09-24 last_activity: 2026-09-24
last_activity_desc: Phase 09 execution started last_activity_desc: Phase 09 execution started
state_head: 50754808f61071e23746f3f36dde8a292a18360b state_head: 0caa86ec0b6d255dd22a55ee5923a40d3a821977
progress: progress:
total_phases: 15 total_phases: 15
completed_phases: 8 completed_phases: 8
total_plans: 67 total_plans: 67
completed_plans: 59 completed_plans: 61
milestone_name: milestone milestone_name: milestone
--- ---
@@ -29,7 +29,7 @@ See: .planning/PROJECT.md (updated 2026-09-16)
## Current Position ## Current Position
Phase: 09 (Backend admin authentication and schema pipeline) — EXECUTING Phase: 09 (Backend admin authentication and schema pipeline) — EXECUTING
Plan: 6 of 12 Plan: 7 of 12
Status: Ready to execute Status: Ready to execute
Last activity: 2026-09-24 — Phase 09 execution started Last activity: 2026-09-24 — Phase 09 execution started
@@ -114,6 +114,7 @@ Progress: [██████████] 100%
| Phase 09 P03 | 25min | 3 tasks | 10 files | | Phase 09 P03 | 25min | 3 tasks | 10 files |
| Phase 09 P04 | 36min | 3 tasks | 13 files | | Phase 09 P04 | 36min | 3 tasks | 13 files |
| Phase 09 P05 | 25min | 3 tasks | 8 files | | Phase 09 P05 | 25min | 3 tasks | 8 files |
| Phase 09 P06 | 29min | 3 tasks | 15 files |
## Accumulated Context ## Accumulated Context
@@ -292,6 +293,10 @@ Recent decisions affecting current work:
- [Phase 09]: Show and update use one not-found body for missing and out-of-scope rows; delete of an absent row is deleted 0 and does not run hooks - [Phase 09]: Show and update use one not-found body for missing and out-of-scope rows; delete of an absent row is deleted 0 and does not run hooks
- [Phase 09]: A bulk selection that matches no scoped row is a no-op; a mixed present and absent selection is a 409 and rolls back - [Phase 09]: A bulk selection that matches no scoped row is a no-op; a mixed present and absent selection is a 409 and rolls back
- [Phase 09]: Controller hook failures return an opaque lifecycle error and do not echo the hook text - [Phase 09]: Controller hook failures return an opaque lifecycle error and do not echo the hook text
- [Phase 09]: Album admin D-14 stores collection_id of the one active frontend user matched by normalized backend email; no album user_id column was added
- [Phase 09]: Winter relation keys match exported Go fields case-insensitively and the served JSON keeps the YAML spelling
- [Phase 09]: Required relation fields stay on the admin form schema and are not save-time column rules
- [Phase 09]: Genre and style admin option values are decimal strings because pact.Option.Value is a string
### Pending Todos ### Pending Todos
@@ -314,6 +319,6 @@ Items acknowledged and carried forward from previous milestone close:
## Session Continuity ## Session Continuity
Last session: 2026-09-24T17:46:43.371Z Last session: 2026-09-24T18:21:51.437Z
Stopped at: Completed 09-05-PLAN.md Stopped at: Completed 09-06-PLAN.md
Resume file: None Resume file: None

View File

@@ -0,0 +1,261 @@
---
phase: 09-backend-admin-authentication-and-schema-pipeline
plan: 06
subsystem: admin
tags: [cabana, albums, winter-yaml, collection-scope, dropdowns, postgres]
requires:
- phase: 09-backend-admin-authentication-and-schema-pipeline
provides: compiled form and list schemas, permission gate, and schema-projected CRUD
provides:
- Registered Albums admin controller with embedded Winter form and list YAML
- Format, genre, and style dropdown options without cross-field leakage
- Exact active-collection binding for album create, update, list, and bulk delete
affects: [09-backend-admin-authentication-and-schema-pipeline, admin-api, phase-10-spa]
actuals:
tokens: 14974
tasks: 3
commits: 2
tech-stack:
added: []
patterns:
- "Winter relation keys match exported Go fields case-insensitively; served JSON keeps the YAML spelling"
- "Album admin scope is the active collection of the one active frontend user with the backend email"
- "Non-scalar required flags stay on the form schema and are not save-time column rules"
key-files:
created:
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/albums_admin_controller.go
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/albums/config_form.yaml
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/albums/config_list.yaml
- ../fonoteka.go/plugins/golem15/fonoteka/models/album/fields.yaml
- ../fonoteka.go/plugins/golem15/fonoteka/models/album/columns.yaml
- ../fonoteka.go/plugins/golem15/fonoteka/classes/backend_album_collection.go
- ../fonoteka.go/plugins/golem15/fonoteka/admin_albums_test.go
modified:
- cabana/http.go
- cabana/query.go
- cabana/list_schema.go
- cabana/crud.go
- ../fonoteka.go/plugins/golem15/fonoteka/models/album.go
- ../fonoteka.go/plugins/golem15/fonoteka/admin.go
key-decisions:
- "The album foreign key written by D-14 is collection_id of the matched frontend user's active collection; albums have no user_id column"
- "Genre and style option values are decimal strings because pact.Option.Value is a string"
- "relation: genre resolves to field Genre while the schema JSON keeps relation genre"
- "Required relation fields remain in the form schema and are not applied as save validation"
patterns-established:
- "Pattern: albumsAdminController resolves *gorm.DB per call from the booted app, never a process-global collection id"
- "Pattern: zero, duplicate, inactive, and no-active-collection email matches share one 422 and name no candidate rows"
requirements-completed: [ADMIN-01, ADMIN-02, ADMIN-04]
coverage:
- id: D1
description: Albums form schema serves every Winter field, locale key, and format scalar in source order for pl and en.
requirement: ADMIN-01
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminForm
status: pass
human_judgment: false
- id: D2
description: Albums registration resolves the album model and embedded assets and rejects a mismatched model or missing directory.
requirement: ADMIN-01
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminRegistration
status: pass
human_judgment: false
- id: D3
description: Denied form, create, and update requests return 403 before album, genre, style, or frontend-user SQL.
requirement: ADMIN-01
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminPermissionOrder
status: pass
human_judgment: false
- id: D4
description: Albums list schema keeps columns, toolbar actions, filters, and locale keys in declaration order.
requirement: ADMIN-02
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminList
status: pass
human_judgment: false
- id: D5
description: Format, genre, and style dropdowns are ordered and do not leak choices across fields.
requirement: ADMIN-01
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminDropdowns
status: pass
human_judgment: false
- id: D6
description: Permitted album lists keep empty, single, equal-value, and adjacent page behavior, and unknown sorts return 422.
requirement: ADMIN-02
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminListEdges
status: pass
human_judgment: false
- id: D7
description: One active normalized email persists that frontend user's active collection id.
requirement: ADMIN-04
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminCollectionMatch
status: pass
human_judgment: false
- id: D8
description: Duplicate, inactive, and no-active-collection email matches return 422 and persist nothing.
requirement: ADMIN-04
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminAmbiguousEmail
status: pass
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminInactiveUser
status: pass
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminCrossCollection
status: pass
human_judgment: false
- id: D9
description: Client user_id and collection_id cannot override the resolved collection, and responses do not echo those keys.
requirement: ADMIN-04
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminProtectedAssociation
status: pass
human_judgment: false
- id: D10
description: Scoped update, show, and atomic bulk delete keep foreign albums unchanged.
requirement: ADMIN-04
verification:
- kind: integration
ref: plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminCRUD
status: pass
human_judgment: false
duration: 29min
completed: 2026-09-24
status: complete
plan_head_before: 3e7738ff32012699bccc4e3f53ea347c87b45f8b
plan_head_after: 0caa86ec0b6d255dd22a55ee5923a40d3a821977
---
# Phase 9 Plan 06: Albums Admin Collection Boundary Summary
**Albums admin form, list, and writes bind only the active collection of the one active frontend user matched by the backend account email.**
## Performance
- **Duration:** 29 min
- **Started:** 2026-09-24T17:50:44Z
- **Completed:** 2026-09-24T18:20:13Z
- **Tasks:** 3
- **Files modified:** 15
## Accomplishments
- Embedded the Winter Albums form and list YAML and served the localized form at `GET /_admin/api/v1/golem15/fonoteka/albums/schema/form`.
- Format options keep Album::FORMATS scalars and phrase keys; genre and style options are active rows ordered by name then id.
- Create and update set `collection_id` from `ResolveBackendAlbumCollectionUser`. Lists, shows, updates, and bulk deletes cannot see another collection.
## TDD Gate Compliance
Each task has a RED `test(09-06)` commit before its GREEN `feat(09-06)` commit. RED evidence checks returned `RED_EVIDENCE_OK` for `TestAlbumsAdminForm`, `TestAlbumsAdminDropdowns`, and `TestAlbumsAdminCollectionMatch`.
## Task Commits
1. **Task 1: Port complete Albums form and controller registration** - `60a4b10` (test, fonoteka.go), `c63146a` (feat, summercms.go), `652bfda` (feat, fonoteka.go)
2. **Task 2: Port Albums list and typed option providers** - `81f1aa4` (test, fonoteka.go), `5de760d` (feat, fonoteka.go)
3. **Task 3: Enforce exact active-collection user resolution** - `6004c6a` (test, fonoteka.go), `0caa86e` (feat, summercms.go), `15dfefa` (feat, fonoteka.go)
The summercms.go ledger `3e7738ff..0caa86e` contains the two framework commits above. Fonoteka commits are in `fonoteka.go` `60a4b10..15dfefa`.
## Files Created/Modified
- `fonoteka.go/plugins/golem15/fonoteka/controllers/albums_admin_controller.go` - Albums controller, permissions, dropdowns, and collection hooks
- `fonoteka.go/plugins/golem15/fonoteka/classes/backend_album_collection.go` - exact normalized email resolution
- `fonoteka.go/plugins/golem15/fonoteka/models/album/fields.yaml` - Winter album form, copied unchanged
- `fonoteka.go/plugins/golem15/fonoteka/models/album/columns.yaml` - Winter album columns, copied unchanged
- `fonoteka.go/plugins/golem15/fonoteka/models/album.go` - `Genre` association and `getFormatOptions`
- `cabana/http.go` - `GET .../schema/form` behind the existing permission check
- `cabana/query.go` and `cabana/list_schema.go` - case-insensitive relation field match
- `cabana/crud.go` - required validation limited to scalar writable fields
- `fonoteka.go/plugins/golem15/fonoteka/admin_albums_test.go` - assembled PostgreSQL proof
## Decisions Made
- D-14 stores `collection_id`, the active collection of the matched frontend user. The albums table has no `user_id` column, so none was added.
- `pact.Option.Value` is a string, so genre and style ids are decimal strings. Format values stay the Winter scalars (`LP`, `EP 7"`, and the rest).
- Served list JSON keeps `relation: genre`. Joins and preloads use the exported Go field `Genre`.
- `artists` stays `required: true` on the form schema. Save validation does not require it, because the admin CRUD path cannot bind a relation.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Embedded the list YAML while registering the controller**
- **Found during:** Task 1
- **Issue:** Activation compiles `config_list.yaml` for every admin controller. Registering Albums with only the form files failed boot.
- **Fix:** Copied the Winter list and columns YAML in the same commit as the form.
- **Files modified:** `controllers/albums/config_list.yaml`, `models/album/columns.yaml`
- **Verification:** `TestAlbumsAdminRegistration` and `TestAlbumsAdminList`
- **Committed in:** `652bfda`
**2. [Rule 2 - Missing Critical] Served the form schema over HTTP**
- **Found during:** Task 1
- **Issue:** Form compilation existed, but no route served it, so permission-denied form requests could not be proven.
- **Fix:** Added `GET /{vendor}/{plugin}/{controller}/schema/form` using the same `protect` gate as the list schema.
- **Files modified:** `cabana/http.go`
- **Verification:** `TestAlbumsAdminForm` and `TestAlbumsAdminPermissionOrder`
- **Committed in:** `c63146a`
**3. [Rule 1 - Bug] Matched Winter relation names to exported Go fields**
- **Found during:** Task 1
- **Issue:** `relation: genre` did not match field `Genre`, so the unchanged columns YAML could not activate.
- **Fix:** Accept an exact field name, otherwise a case-insensitive relation match. JSON still says `genre`.
- **Files modified:** `cabana/list_schema.go`, `cabana/query.go`, `models/album.go`
- **Verification:** `go test ./cabana` and `TestAlbumsAdminList`
- **Committed in:** `c63146a`, `652bfda`
**4. [Rule 2 - Missing Critical] Did not save-validate unbound relation required flags**
- **Found during:** Task 3
- **Issue:** `artists.required` made every album create 422 before the collection hook ran. The field is not a writable column.
- **Fix:** Merge `required` into save rules only for scalar writable field types. The schema JSON is unchanged.
- **Files modified:** `cabana/crud.go`
- **Verification:** `go test ./cabana` and `TestAlbumsAdminCollectionMatch`
- **Committed in:** `0caa86e`
---
**Total deviations:** 4 auto-fixed (1 bug, 2 missing critical, 1 blocking)
**Impact on plan:** Required for unchanged Winter YAML to boot and for album creates to reach the collection hook. No new album columns and no extra controllers.
## Issues Encountered
`TestPhase8RedMCPMe` and `TestOAuthToolsMeRouteIsolation` fail because `GET /_admin/api/v1/auth/me` ends in `/me`. That route was already mounted with the admin API before this plan. It was left unchanged.
`ADMIN-01`, `ADMIN-02`, and `ADMIN-04` stay pending in `REQUIREMENTS.md`. Later plans in this phase still declare them.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
Ready for 09-07. Albums is the only new admin controller. Artists, Styles, and Collections controllers were not added. Genre remains the existing list tracer.
## Self-Check: PASSED
- FOUND: `fonoteka.go` `60a4b10`, `652bfda`, `81f1aa4`, `5de760d`, `6004c6a`, `15dfefa`
- FOUND: `summercms.go` `c63146a`, `0caa86e`
- FOUND: albums controller, form YAML, columns YAML, and `backend_album_collection.go`
---
*Phase: 09-backend-admin-authentication-and-schema-pipeline*
*Completed: 2026-09-24*