20 KiB
phase, plan, subsystem, tags, requires, provides, affects, actuals, plan_head_before, plan_head_after, app_plan_head_before, app_plan_head_after, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | actuals | plan_head_before | plan_head_after | app_plan_head_before | app_plan_head_after | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | duration | completed | status | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10-admin-vue-spa | 02 | admin |
|
|
|
|
|
e9b48d4720 |
9f296b0484 |
bb4cd7a17584fe52f43c9884ec555e319431c12f | cb6727f3c284265d6254c42e8c00db3f7ab4655e |
|
|
|
|
|
|
50min | 2026-09-27 | complete |
Phase 10 Plan 02: Admin backend contract Summary
The cabana admin API now serves everything the SPA screens need: relation choices and relation saves with labels (read-only protected owner), framework backend::lang strings with an override layer and a public bundle, per-controller messages as CLDR plural forms, a declarative [create, delete] toolbar, model-backed filter choices, and an OpenAPI document whose every route is proven against the real handler output.
Performance
- Duration: 50 min
- Started: 2026-09-27T13:38:29Z
- Completed: 2026-09-27T14:28:07Z
- Tasks: 3
- Files modified: 36 in summercms.go, 27 in fonoteka.go
Accomplishments
- Relations (D-17, D-18, D-26). A controller declares
AdminFieldRelations()with a belongsTo foreign key or a belongsToMany pivot (optional order column, label column). Activation rejects a relation field without a contract, a missing column, an unknown kind, duplicates and orphans, naming plugin, controller and field.GET .../fields/{field}/optionspages{value, label}choices through the controller'sRelationExtendOptionsQuery. On save, present relation keys are revalidated through that same scoped query after the Before hook (422 and a full rollback otherwise). belongsTo sets the key before the row write, and belongsToMany deletes and bulk-inserts pivot rows in submitted order. Show, create and update return the values indataand the labels inmeta.labels. A belongsTo on a protected fill key (Collectionsowner_id) is servedreadOnly, has no options endpoint and is never written. - Strings (D-20).
phrasebook/backend/lang/{pl,en}/lang.yamlcarries the framework admin copy, including every key the SPA already uses, with CLDR plural maps. It loads as namespacebackend.pact.HasLangOverridestrees can replace keys and add locales. Activation fails when abackend::key cannot convert to plural forms. The publicGET /langreturns onlybackend::lang.*for the request locale over the fallback, withCache-Control: no-cache. - Copy and toolbar (D-13, D-14, D-24).
messages:blocks on list, form and relation configs are decoded strictly. Omitted keys take framework defaults, every response carries a complete messages object of CLDR form maps, and activation fails on a missing phrase key.toolbar.buttonsis an ordered[create, delete]list. The Winter string form, duplicates, unknown actions anddeletewithout checkboxes fail at boot, andcreateis dropped when there is no form. The form schema also serves the raw Winter redirects. - Filters (D-27). A scope filter's model must implement
pact.FilterOptions.GET .../filters/{scope}/optionsserves localized choices behind the controller permission. - Typed API (D-15, D-16). Every route documents a concrete success schema and its error responses, and
SuccessEnvelopeis gone.TestPhase10OpenAPIConformancedrives all 25 inventoried routes through the assembled router on PostgreSQL and decodes each body with unknown fields disallowed. It fails when a route has no case. - fonoteka. The albums controller has genre and artists contracts, and the collections controller has a read-only owner. All five controllers declare
[create, delete]and messages, and the editors relation has its own copy. The new keys come with Polish plurals. Assembled tests cover relations, the read-only owner, Polish copy and the YAML-exact schemas.
Task Commits
- Task 1 (tracer): relation options and saves with labels -
fe04dbc(feat, summercms.go),2914892(feat, fonoteka.go). Preceded by84dbda1(test, fonoteka.go), which repairs pre-existing Phase 9 test failures (see Deviations). - Task 2: backend strings, messages, declarative toolbar -
c87148a(feat, summercms.go),648bff6(feat, fonoteka.go) - Task 3: filter choices, full typing, conformance -
9f296b0(feat, summercms.go),cb6727f(test, fonoteka.go)
Plan metadata: recorded in the docs commit that adds this file.
Tracer gate (Task 1): the Task 1 <verify> was re-run end to end after its commits and passed (human_verify_mode end-of-phase, automated verify), then Task 2 started.
Decisions Made
See key-decisions in the frontmatter. The most consequential one is the shared six-segment GET pattern. It keeps the D-17 and D-27 paths exactly as decided without touching the Phase 9 relation route.
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 - Blocking] ServeMux conflict between the new options routes and the relation list route
- Found during: Task 1
- Issue:
GET {api}/{vendor}/{plugin}/{controller}/fields/{field}/optionsand the Phase 9GET .../{id}/relations/{name}both match paths like.../fields/relations/options, and neither is more specific. Go's ServeMux panics at registration (verified with a probe). The same applies tofilters/{scope}/options. - Fix: one mounted pattern
GET .../{id}/{segment}/{name}with identifier constraints, dispatched byservice.nestedGeton the literal segments. A numeric id never equalsfieldsorfilters, and anything else is the JSONnot_foundenvelope. The route inventories in both repos gainedmountedkeys, and the OpenAPI document keeps the three logical paths. - Files:
cabana/http.go,cabana/security_coverage_test.go,../fonoteka.go/.../admin_phase09_security_test.go - Commits:
fe04dbc,2914892,9f296b0
2. [Rule 3 - Blocking] Twelve fonoteka plugin tests were already failing before this plan
- Found during: Task 1 (the plan's verify runs
TestAlbumsAdmin.*andTestCollectionsAdmin.*) - Issue: On untouched HEAD copies of both repos,
go testinplugins/golem15/fonotekafailed 12 tests. Form and list tests pinned raw phrase keys and identical pl/en bodies, which the 10-01 lang catalog now translates. The /me route-isolation check also flagged the backend admin/plytadmin/api/v1/auth/me. The fonoteka rootgo test ./...does not include the plugin modules, so 10-01 did not see these failures. - Fix: those tests now pin the resolved en labels plus a localized pl body, and the isolation check allows the admin profile route. The tracer genre list searches for its own row, because genres created by other tests can push it past page 1.
- Commits:
84dbda1(artists, genres, styles, tracer, oauth tools); album and collection pins are in2914892 - Result: every fonoteka plugin module package now passes.
3. [Rule 2 - Correctness] jsonScalar and fieldContext had no JSON decoders
- Found during: Task 3 (conformance test)
- Fix:
UnmarshalJSONfor both, accepting exactly their served shapes, so a FormView decodes back into its documented type. - Commit:
9f296b0
4. [Rule 3 - Blocking] boardwalk/dist drifted on API-only changes
- Found during: Task 3 (
check-admin-dist.sh) - Issue: Tailwind v4 scanned
schema.d.tsandopenapi/admin.json, so new description prose produced new utility classes. - Fix:
@source notfor both inadmin/src/styles/main.css. The fresh build equals the committed dist again, so no dist change was needed. - Commit:
9f296b0
5. [Rule 3] Phase 9 contract test accepted only 200
- Fix:
TestPhase09ContractInventoryaccepts 200 or 201 (create is now documented as 201, matching the handler) and requires 401 only for login and refresh among public routes (/langis public and never 401). - Commits:
fe04dbc,c87148a
6. [Process] TDD RED evidence
- RED runs were recorded and verified
RED_EVIDENCE_OKbygsd-tools check tdd-red-evidencefor:- TestPhase10AlbumRelations (with TestPhase10CollectionOwnerReadOnly)
- TestPhase10RelationSave (with RelationOptions, ForgedID and Boot in the same run)
- TestPhase10Forms (with LangOverride and SPAKeysResolve)
- The cabana Messages, Toolbar, Bundle, FilterOptions and OpenAPIConformance tests and the fonoteka ControllerCopy and Controllers tests were written with or right after their implementation, so no failing run was captured for them.
- As in 10-01, tests and code were committed together per repository so that every commit stays green (CLAUDE.md), so there are no separate
test(...)RED commits.
7. [Acceptance note] grep -rniE 'golem15|album' cabana/relation_field.go
- This prints one line: the framework's own import path
git.golem15.com/golem15/summercms/pact. No plugin table, key, pivot or domain name appears in the file.
Total deviations: 5 auto-fixed (4 blocking, 1 correctness), 2 process/acceptance notes. Impact on plan: None on scope. The mounted route table differs from the documented one by one shared pattern.
Issues Encountered
- The known fonoteka.go
parityfailures (TestMigrateSeedsCanonicalGenres,TestSchemaMatchesPHPSnapshot) remain, as logged indeferred-items.md. Everything else passesgo vetandgo testin both repositories, including every fonoteka plugin module. gofmt -l internal/build/registry.gois still pre-existing (deferred).
Known Stubs
admin/src/app/i18n.ts:t()still returns keys because the SPA does not load the bundle yet. The server now serves it atGET {prefix}/api/v1/lang, and Plan 10-03 wiressetBundleat startup. This was intentional per D-28.
User Setup Required
None.
Next Phase Readiness
- Plan 10-03 can render lists, forms, filters and settings entirely from typed schemas. It should use:
ListSchema.messages,toolbarButtonsandfilterswith/filters/{scope}/optionsFormView.messagesandredirects, plusFormField.multipleandreadOnly/fields/{field}/optionsfor relation inputsRecordEnvelope(dataplusmeta.labels) for show and saveGET /langfor the UI strings
- Plan 10-04 has
RelationSchema.messagesfor the editors manager. - Any new admin route needs an inventory entry and a
TestPhase10OpenAPIConformancecase.
Phase: 10-admin-vue-spa Completed: 2026-09-27
Self-Check: PASSED
All created files listed above exist; commits fe04dbc, c87148a and 9f296b0 (summercms.go) and 84dbda1, 2914892, 648bff6 and cb6727f (fonoteka.go) are present.