docs(09-06): complete albums admin collection boundary plan
This commit is contained in:
@@ -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.
|
||||
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
|
||||
|
||||
Plans:
|
||||
@@ -396,7 +396,7 @@ Plans:
|
||||
- [x] 09-05-PLAN.md — Deliver schema-projected CRUD and transactional bulk deletion
|
||||
|
||||
**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)*
|
||||
- [ ] 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 |
|
||||
| 7. User plugin and authentication | 8/8 | 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 | - |
|
||||
| 11. Jobs, realtime and search infrastructure | 0/TBD | Not started | - |
|
||||
| 12. Płytarium API — Collections and Albums | 0/TBD | Not started | - |
|
||||
|
||||
@@ -4,16 +4,16 @@ milestone: v1.0
|
||||
current_phase: 09
|
||||
current_phase_name: Backend admin authentication and schema pipeline
|
||||
status: executing
|
||||
stopped_at: Completed 09-05-PLAN.md
|
||||
last_updated: "2026-09-24T17:46:54.525Z"
|
||||
stopped_at: Completed 09-06-PLAN.md
|
||||
last_updated: "2026-09-24T18:21:51.771Z"
|
||||
last_activity: 2026-09-24
|
||||
last_activity_desc: Phase 09 execution started
|
||||
state_head: 50754808f61071e23746f3f36dde8a292a18360b
|
||||
state_head: 0caa86ec0b6d255dd22a55ee5923a40d3a821977
|
||||
progress:
|
||||
total_phases: 15
|
||||
completed_phases: 8
|
||||
total_plans: 67
|
||||
completed_plans: 59
|
||||
completed_plans: 61
|
||||
milestone_name: milestone
|
||||
---
|
||||
|
||||
@@ -29,7 +29,7 @@ See: .planning/PROJECT.md (updated 2026-09-16)
|
||||
## Current Position
|
||||
|
||||
Phase: 09 (Backend admin authentication and schema pipeline) — EXECUTING
|
||||
Plan: 6 of 12
|
||||
Plan: 7 of 12
|
||||
Status: Ready to execute
|
||||
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 P04 | 36min | 3 tasks | 13 files |
|
||||
| Phase 09 P05 | 25min | 3 tasks | 8 files |
|
||||
| Phase 09 P06 | 29min | 3 tasks | 15 files |
|
||||
|
||||
## 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]: 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]: 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
|
||||
|
||||
@@ -314,6 +319,6 @@ Items acknowledged and carried forward from previous milestone close:
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-09-24T17:46:43.371Z
|
||||
Stopped at: Completed 09-05-PLAN.md
|
||||
Last session: 2026-09-24T18:21:51.437Z
|
||||
Stopped at: Completed 09-06-PLAN.md
|
||||
Resume file: None
|
||||
|
||||
@@ -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*
|
||||
Reference in New Issue
Block a user