docs(09-10): complete collections relation manager plan
This commit is contained in:
@@ -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*
|
||||
Reference in New Issue
Block a user