Files
summercms/.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-06-SUMMARY.md
2026-09-24 20:22:27 +02:00

12 KiB

phase, plan, subsystem, tags, requires, provides, affects, actuals, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status, plan_head_before, plan_head_after
phase plan subsystem tags requires provides affects actuals tech-stack key-files key-decisions patterns-established requirements-completed coverage duration completed status plan_head_before plan_head_after
09-backend-admin-authentication-and-schema-pipeline 06 admin
cabana
albums
winter-yaml
collection-scope
dropdowns
postgres
phase provides
09-backend-admin-authentication-and-schema-pipeline compiled form and list schemas, permission gate, and schema-projected CRUD
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
09-backend-admin-authentication-and-schema-pipeline
admin-api
phase-10-spa
tokens tasks commits
14974 3 2
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
created modified
../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
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
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
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
ADMIN-01
ADMIN-02
ADMIN-04
id description requirement verification human_judgment
D1 Albums form schema serves every Winter field, locale key, and format scalar in source order for pl and en. ADMIN-01
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminForm pass
false
id description requirement verification human_judgment
D2 Albums registration resolves the album model and embedded assets and rejects a mismatched model or missing directory. ADMIN-01
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminRegistration pass
false
id description requirement verification human_judgment
D3 Denied form, create, and update requests return 403 before album, genre, style, or frontend-user SQL. ADMIN-01
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminPermissionOrder pass
false
id description requirement verification human_judgment
D4 Albums list schema keeps columns, toolbar actions, filters, and locale keys in declaration order. ADMIN-02
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminList pass
false
id description requirement verification human_judgment
D5 Format, genre, and style dropdowns are ordered and do not leak choices across fields. ADMIN-01
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminDropdowns pass
false
id description requirement verification human_judgment
D6 Permitted album lists keep empty, single, equal-value, and adjacent page behavior, and unknown sorts return 422. ADMIN-02
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminListEdges pass
false
id description requirement verification human_judgment
D7 One active normalized email persists that frontend user's active collection id. ADMIN-04
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminCollectionMatch pass
false
id description requirement verification human_judgment
D8 Duplicate, inactive, and no-active-collection email matches return 422 and persist nothing. ADMIN-04
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminAmbiguousEmail pass
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminInactiveUser pass
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminCrossCollection pass
false
id description requirement verification human_judgment
D9 Client user_id and collection_id cannot override the resolved collection, and responses do not echo those keys. ADMIN-04
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminProtectedAssociation pass
false
id description requirement verification human_judgment
D10 Scoped update, show, and atomic bulk delete keep foreign albums unchanged. ADMIN-04
kind ref status
integration plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminCRUD pass
false
29min 2026-09-24 complete 3e7738ff32 0caa86ec0b

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.

Plan metadata: 2a955d6 (docs: complete albums admin collection boundary plan)

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