diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index da0211c..654470b 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -402,7 +402,7 @@ Plans: - [x] 09-07-PLAN.md — Port the Artists backend controller - [x] 09-08-PLAN.md — Complete the Genres controller with form/list parity - [x] 09-09-PLAN.md — Port the Styles controller and typed provider/filter behavior -- [ ] 09-10-PLAN.md — Deliver Collections and the typed relation manager +- [x] 09-10-PLAN.md — Deliver Collections and the typed relation manager **Wave 8** *(blocked on Wave 7 completion)* - [ ] 09-11-PLAN.md — Complete permissions, navigation, and singleton settings @@ -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 | 9/12 | In Progress| | +| 9. Backend admin authentication and schema pipeline | 10/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 | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 07040d4..89d9a0e 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,17 +5,17 @@ milestone_name: milestone current_phase: 09 current_phase_name: Backend admin authentication and schema pipeline status: executing -stopped_at: Completed 09-09-PLAN.md -last_updated: "2026-09-26T13:09:59.401Z" +stopped_at: Completed 09-10-PLAN.md +last_updated: "2026-09-26T19:35:51.309Z" last_activity: 2026-09-26 -last_activity_desc: Phase 09 execution continued through the Styles controller -state_head: e9c07223376519f1636dad557cccb6cf0ddd1604 +last_activity_desc: Phase 09 execution continued through Collections and the typed relation manager +state_head: 12081c153464a632d3a89385f915571a9a14c910 progress: total_phases: 15 completed_phases: 8 total_plans: 67 - completed_plans: 64 - percent: 96 + completed_plans: 65 + percent: 97 --- # Project State @@ -30,11 +30,11 @@ See: .planning/PROJECT.md (updated 2026-09-16) ## Current Position Phase: 09 (Backend admin authentication and schema pipeline) — EXECUTING -Plan: 10 of 12 +Plan: 11 of 12 Status: Ready to execute Last activity: 2026-09-26 -Progress: [██████████] 96% +Progress: [██████████] 97% ## Performance Metrics @@ -119,6 +119,7 @@ Progress: [██████████] 96% | Phase 09 P07 | 2h20m | 2 tasks | 9 files | | Phase 09 P08 | 2h28m | 2 tasks | 4 files | | Phase 09 P09 | 11h 48m | 2 tasks | 9 files | +| Phase 09 P10 | 2h 20m | 3 tasks | 17 files | ## Accumulated Context @@ -308,6 +309,7 @@ Recent decisions affecting current work: - [Phase 09]: The tracked Style source declares no dropdown provider or filter, so production YAML stays exact while isolated compiler fixtures prove typed scalars and provider rejection - [Phase 09]: Style slug retains the tracked update-only readonly schema and is generated on create by the model lifecycle - [Phase 09]: Style equal-value list ordering is explicitly stabilized by the shared primary-key tie-breaker +- [Phase 09]: Collection-specific pivot identifiers and stamps remain plugin-owned behind a typed relation contract. — Cabana can validate and execute relations generically without hardcoding Fonoteka tables, columns, roles, or payload behavior. ### Pending Todos @@ -330,6 +332,6 @@ Items acknowledged and carried forward from previous milestone close: ## Session Continuity -Last session: 2026-09-26T13:09:59.379Z -Stopped at: Completed 09-09-PLAN.md +Last session: 2026-09-26T19:35:51.276Z +Stopped at: Completed 09-10-PLAN.md Resume file: None diff --git a/.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-10-SUMMARY.md b/.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-10-SUMMARY.md new file mode 100644 index 0000000..c97b4cf --- /dev/null +++ b/.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-10-SUMMARY.md @@ -0,0 +1,191 @@ +--- +phase: 09-backend-admin-authentication-and-schema-pipeline +plan: 10 +subsystem: admin +tags: [cabana, relations, winter-yaml, postgres, permissions, transactions] + +requires: + - phase: 09-backend-admin-authentication-and-schema-pipeline + provides: Compiled form/list schemas, permission gates, CRUD, and bulk services +provides: + - Complete Collection form/list assets and registered permission-gated controller + - Typed relation schemas plus stable linked and candidate queries + - Transactional, scoped, idempotent explicit pivot link and unlink endpoints +affects: [09-backend-admin-authentication-and-schema-pipeline, admin-api, phase-10-spa] + +actuals: + tokens: 30000 + tasks: 3 + commits: 5 + +tech-stack: + added: [] + patterns: + - "Plugin-owned relation contracts provide target, pivot, foreign-key, column, exclusion, and hook metadata" + - "Legacy public schema columns map explicitly to physical related-model columns without changing the API contract" + +key-files: + created: + - cabana/relation.go + - cabana/relation_test.go + - ../fonoteka.go/plugins/golem15/fonoteka/controllers/collections/config_relation.yaml + modified: + - cabana/http.go + - cabana/query.go + - cabana/registry.go + - pact/capabilities.go + - ../fonoteka.go/plugins/golem15/fonoteka/controllers/collections_admin_controller.go + - ../fonoteka.go/plugins/golem15/fonoteka/admin_collections_test.go + +key-decisions: + - "Collection editor pivot knowledge remains plugin-owned; Cabana compiles and executes only finite typed relation metadata" + - "The source schema's logical username column maps to the current user model's email column for list and relation operations" + - "Candidate eligibility is active users excluding the owner and already-linked users; link revalidates the same scope transactionally" + +patterns-established: + - "Pattern: relation mutations normalize identifiers, lock the parent, revalidate candidates, and explicitly write model-owned pivots in one transaction" + - "Pattern: idempotent relation replay neither duplicates nor restamps an existing pivot" + +requirements-completed: [ADMIN-01, ADMIN-02, ADMIN-03, ADMIN-04] + +coverage: + - id: D1 + description: "The complete Collection form/list schema compiles, preserves source order and permissions, and replaces the PHP partial with a typed relation-manager field." + requirement: ADMIN-01 + verification: + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminForm + status: pass + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminListCRUD + status: pass + human_judgment: false + - id: D2 + description: "Linked and candidate relation queries return stable non-null arrays and exclude owner, inactive, and already-linked users at the database boundary." + requirement: ADMIN-03 + verification: + - kind: unit + ref: cabana/relation_test.go#TestRelationSchema + status: pass + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminRelationEdges + status: pass + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminCrossScope + status: pass + human_judgment: false + - id: D3 + description: "Permissioned link/unlink mutations reject forged payloads, stamp model-owned pivot fields, converge under replay/concurrency, and remain transactionally scoped." + requirement: ADMIN-03 + verification: + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminRelationPermissions + status: pass + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminLink + status: pass + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminForgedPivot + status: pass + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminConcurrent + status: pass + human_judgment: false + - id: D4 + description: "Collection CRUD and bulk operations retain validation, lifecycle, rollback, duplicate-normalization, and permission behavior on PostgreSQL." + requirement: ADMIN-04 + verification: + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminListCRUD + status: pass + - kind: integration + ref: plugins/golem15/fonoteka/admin_collections_test.go#TestCollectionsAdminBulk + status: pass + human_judgment: false + +duration: 2h 20m +completed: 2026-09-26 +status: complete +--- + +# Phase 9 Plan 10: Collections and Typed Relation Manager Summary + +**Collection administration now uses source-compatible typed schemas and a secure, plugin-owned relation contract for deterministic editor management.** + +## Performance + +- **Duration:** 2h 20m +- **Started:** 2026-09-26T19:14:00+02:00 +- **Completed:** 2026-09-26T21:34:20+02:00 +- **Tasks:** 3 +- **Files modified:** 17 + +## Accomplishments + +- Ported the complete Collection form/list controller and replaced the executable PHP partial with a typed relation-manager field. +- Added compiled relation schemas and stable linked/candidate endpoints with database-level eligibility exclusions. +- Added explicit transactional pivot link/unlink behavior with permission-first routing, hook-owned stamps, replay safety, and concurrency coverage. +- Proved Collection list, CRUD, bulk, relation, permission, forgery, scope, and edge behavior against PostgreSQL. + +## Task Commits + +1. **Task 1: Port Collection form and replace partial with relation-manager** - `2b144fd`, `662ec9f` +2. **Task 2: Compile list and relation schemas with stable edge semantics** - `12081c1`, `23793b7` +3. **Task 3: Execute permissioned explicit pivot link and unlink** - `48f486d` + +## Files Created/Modified + +- `cabana/relation.go` - Relation compiler, query service, validation, and transactional mutations. +- `cabana/relation_test.go` - Framework relation schema and behavior contracts. +- `cabana/http.go` - Permissioned relation schema/list/link/unlink routes. +- `cabana/query.go` - Explicit logical-to-physical related-column mapping. +- `pact/capabilities.go` - Finite controller capability for legacy relation-column mapping. +- `controllers/collections/config_relation.yaml` - Winter-shaped editor view/manage schema. +- `controllers/collections_admin_controller.go` - Collection relation contract, candidate scope, pivot hook, and owner resolution. +- `admin_collections_test.go` - Assembled PostgreSQL coverage for Collection and relation behavior. + +## Decisions Made + +- Kept all Collection-specific pivot identifiers and stamps out of Cabana by exposing them through a typed plugin contract. +- Preserved the tracked `username` schema key while mapping it to the current user table's `email` column at the controller boundary. +- Treated frontend users as global candidates, then enforced source-derived active, owner, and already-linked exclusions before mutation. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] Added explicit legacy relation-column mapping** + +- **Found during:** Task 3 integration verification. +- **Issue:** The tracked Collection list selects `owner.username`, but the current Go user model and table expose only `email`, causing search/list SQL to reference a nonexistent column. +- **Fix:** Added the finite `ListRelationColumnMapper` controller capability and mapped only `owner.username` to `email` while retaining the public source schema. +- **Files modified:** `pact/capabilities.go`, `cabana/query.go`, `cabana/http.go`, `collections_admin_controller.go`. +- **Verification:** Full Cabana suite and `^TestCollectionsAdmin` suite pass. +- **Committed in:** `12081c1`, `23793b7`. + +--- + +**Total deviations:** 1 auto-fixed (1 blocking compatibility issue) +**Impact on plan:** Necessary for source fidelity against the current physical user schema; no feature scope expansion. + +## Issues Encountered + +- The PHP relation display key predates the Go user schema. The explicit controller mapping resolves it without framework hardcoding or YAML drift. + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- Collection and relation APIs are complete and ready for the later Admin Vue SPA. +- Ready for 09-11 notification/settings administration; no blockers. + +## Self-Check: PASSED + +- `go test ./cabana -count=1` +- `go test ./plugins/golem15/fonoteka -run '^TestCollectionsAdmin' -count=1` + +--- +*Phase: 09-backend-admin-authentication-and-schema-pipeline* +*Completed: 2026-09-26*