--- phase: 09-backend-admin-authentication-and-schema-pipeline plan: 03 subsystem: admin tags: [yaml, goccy, phrasebook, cabana, admin-schema, scaffolding] requires: - phase: 09-backend-admin-authentication-and-schema-pipeline provides: cabana list compiler, admin controller registry, and phrasebook translator provides: - Typed Winter form schema compiled at activation with source order and JSON scalar types - Per-request localization of labels, comments, tabs, emptyOption, and option labels - make:admin-controller output that matches the Winter controller/model YAML split affects: [09-backend-admin-authentication-and-schema-pipeline, admin-api, phase-10-spa] actuals: tokens: 13818 tasks: 3 commits: 6 tech-stack: added: [] patterns: - "Form IR caches source phrase keys; Localize copies the schema per request" - "config_form.yaml is compiled only when the file is present, so list-only controllers still boot" - "Dropdown method names require DropdownOptions on the controller or its NewRecord model" key-files: created: - cabana/schema_types.go - cabana/form_schema.go - cabana/form_schema_test.go modified: - cabana/contracts.go - cabana/registry.go - cabana/schema.go - phrasebook/translator.go - internal/build/artifact.go - internal/build/stubs/artifacts.tmpl - internal/build/build_test.go key-decisions: - "Cached form schemas keep source phrase keys; each response localizes a copy and records meta.locale" - "Absent config_form.yaml does not fail activation, so the 09-01 Genre list controller still boots" - "YAML option keys keep their JSON scalar type; method options call DropdownOptions(field) with the exact field name" - "make:admin-controller writes controllers//config_form.yaml and config_list.yaml pointing at models//fields.yaml and columns.yaml" patterns-established: - "Pattern: boot compiler walks the YAML AST so field order and option scalar types survive DisallowUnknownField" - "Pattern: a missing dropdown provider fails with plugin, controller, file, and the exact method name" requirements-completed: [ADMIN-01] coverage: - id: D1 description: Every locked form field and layout hint compiles in source order with Winter JSON keys and typed scalars. requirement: ADMIN-01 verification: - kind: unit ref: cabana/form_schema_test.go#TestFormSchemaCompile status: pass human_judgment: false - id: D2 description: Empty fields serialize as an array, a single field stays one element, and repeated compilation is byte-stable. requirement: ADMIN-01 verification: - kind: unit ref: cabana/form_schema_test.go#TestFormSchemaEmpty status: pass - kind: unit ref: cabana/form_schema_test.go#TestFormSchemaSingle status: pass - kind: unit ref: cabana/form_schema_test.go#TestFormSchemaOrdering status: pass human_judgment: false - id: D3 description: Unknown keys, unknown types, duplicates, path escape, a mismatched modelClass, partials, and missing assets fail with plugin, controller, and file context. requirement: ADMIN-01 verification: - kind: unit ref: cabana/form_schema_test.go#TestFormSchemaRejects status: pass human_judgment: false - id: D4 description: One cached schema localizes independently in pl and en, including Accept-Language parent fallback, app.locale, and raw-key fallback. requirement: ADMIN-01 verification: - kind: unit ref: cabana/form_schema_test.go#TestFormSchemaLocalization status: pass - kind: unit ref: cabana/form_schema_test.go#TestFormSchemaLocaleIsolation status: pass human_judgment: false - id: D5 description: YAML option maps keep order and scalar type, and a string options method requires DropdownOptions at boot. requirement: ADMIN-01 verification: - kind: unit ref: cabana/form_schema_test.go#TestFormSchemaDropdownOptions status: pass human_judgment: false - id: D6 description: summer make:admin-controller emits the Winter controller/model YAML split, compiles through the strict loader, and refuses a duplicate without partial files. requirement: ADMIN-01 verification: - kind: unit ref: internal/build/build_test.go#TestMakeAdminControllerLayout status: pass human_judgment: false duration: 25min completed: 2026-09-24 status: complete plan_head_before: af3312aa9260daa7106f66dff892924db965b930 plan_head_after: 68715fc2603f129f0e9f05d8d1db23f5d7594e12 --- # Phase 9 Plan 03: Typed Winter form schema Summary **Winter form YAML now compiles into an ordered typed JSON schema, each response localizes its own copy, and `make:admin-controller` emits that same layout.** ## Performance - **Duration:** 25 min - **Started:** 2026-09-24T16:01:20Z - **Completed:** 2026-09-24T16:25:51Z - **Tasks:** 3 - **Files modified:** 10 ## Accomplishments - `config_form.yaml` and `fields.yaml` compile once at cabana activation into text, textarea, number, checkbox, switch, dropdown, relation, and relation-manager fields, in source order, with Winter JSON spelling. - Empty fields are `[]`, option values keep string, number, and boolean JSON types, and `type: partial` plus unknown keys, types, path escape, and a mismatched `modelClass` fail before serving. - Labels, comments, tabs, emptyOption, and option labels resolve per request through phrasebook. The cache stays source keys. `meta.locale` is the selected request locale, or `app.locale` when the request has none. - `summer make:admin-controller` writes `controllers//config_form.yaml` and `config_list.yaml` that point at `models//fields.yaml` and `columns.yaml`. A duplicate asset fails before any new file is written. ## Task Commits Each task was committed atomically. `commits: 6` is `git rev-list --count` from the plan ledger. 1. **Task 1: Strict ordered form compilation (RED)** - `ea3f070` (test) 2. **Task 1: Strict ordered form compilation (GREEN)** - `4c814e5` (feat) 3. **Task 2: Locale and dropdown options (RED)** - `ad1b760` (test) 4. **Task 2: Locale and dropdown options (GREEN)** - `da8828e` (feat) 5. **Task 3: Winter admin scaffolding (RED)** - `af8e58a` (test) 6. **Task 3: Winter admin scaffolding (GREEN)** - `68715fc` (feat) **Plan metadata:** pending docs commit ## Files Created/Modified - `cabana/schema_types.go` - form schema, field, option, and localized view types - `cabana/form_schema.go` - strict boot compiler and request-time localizer - `cabana/form_schema_test.go` - golden, empty/single/order, rejection, locale, and dropdown tests - `cabana/registry.go` - compiles a form when `config_form.yaml` is present - `cabana/contracts.go` - compiled controllers carry the form schema - `cabana/schema.go` - exported `CompileList` for the same strict list loader - `phrasebook/translator.go` - `Locale()` reports the configured app locale - `internal/build/artifact.go` - Winter directory split and duplicate checks for the full file set - `internal/build/stubs/artifacts.tmpl` - `config_form.yaml` and `config_list.yaml` stubs - `internal/build/build_test.go` - layout, strict compile, registry stability, and partial-write tests ## Decisions Made - The cached schema stores phrase keys. `Localize` copies fields and options, so one request's locale cannot change another's result or the boot cache. - `meta.locale` is the primary Accept-Language tag when the request has one (`pl-PL,en;q=0.5` becomes `pl-PL`). Phrasebook still parent-falls back to `pl`. With no request locale, meta uses the translator's app locale. - A controller with only `config_list.yaml` keeps activating. The 09-01 Genre tracer has no form yet. A present but invalid form still fails activation. - Dropdown `options:` maps keep declaration order and key scalar type. A string such as `getFormatOptions` is a method name, compared exactly, and the controller or `NewRecord()` model must implement `DropdownOptions`. Only the label is translated. - Scaffolded `modelClass` is the controller ident (`Albums`), matching `ModelName()`, and `ConfigDir()` stays `controllers/`. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 2 - Missing Critical] Exposed the configured app locale** - **Found during:** Task 2 (locale and dropdown options) - **Issue:** `phrasebook.Translator` kept `app.locale` unexported, so a request with no Accept-Language could not put the real app locale in `meta.locale`. - **Fix:** Added `Translator.Locale()`. Cabana does not read config itself. - **Files modified:** `phrasebook/translator.go`, `cabana/form_schema.go` - **Verification:** `TestFormSchemaLocalization/parent_and_app_locale` passed - **Committed in:** `da8828e` **2. [Rule 3 - Blocking] Exported the list compiler for scaffold verification** - **Found during:** Task 3 (Winter admin scaffolding) - **Issue:** Generated `config_list.yaml` had to compile through the strict loader, and `compileList` was unexported to the build tests. - **Fix:** Exported `cabana.CompileList` as a wrapper. No behavior change for activation. - **Files modified:** `cabana/schema.go`, `internal/build/build_test.go` - **Verification:** `TestMakeAdminControllerLayout/winter_files` passed - **Committed in:** `af8e58a` (export) and `68715fc` (scaffold) --- **Total deviations:** 2 auto-fixed (1 missing critical, 1 blocking) **Impact on plan:** Both keep the locked locale and loader contracts testable. No new dependency and no response-shape change outside the form schema this plan owns. ## TDD Gate Compliance | Gate | Commit | Result | |------|--------|--------| | RED task 1 | `ea3f070` test(09-03) | `TestFormSchemaCompile` failed because compilation was not implemented | | GREEN task 1 | `4c814e5` feat(09-03) | compile, empty, single, ordering, and reject tests passed | | RED task 2 | `ad1b760` test(09-03) | `TestFormSchemaLocalization` failed because localization was not implemented | | GREEN task 2 | `da8828e` feat(09-03) | localization, dropdown, and race-isolated locale tests passed | | RED task 3 | `af8e58a` test(09-03) | `TestMakeAdminControllerLayout` failed on the old controller-local YAML paths | | GREEN task 3 | `68715fc` feat(09-03) | Winter layout, strict compile, and duplicate-without-partial-write passed | `gsd_run check tdd-red-evidence` returned `RED_EVIDENCE_OK` for all three RED runs. Go's test output is not TAP, so each evidence record appends a TAP trailer naming the test the go harness failed. ## Authentication Gates None. ## Issues Encountered None. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - Ready for 09-04. Form compilation, localization, and the Winter scaffold layout are in place. - ADMIN-01 stays pending in REQUIREMENTS.md until the later plans that also declare it have summaries. - List-only controllers remain valid until those plans add `config_form.yaml`. ## Self-Check: PASSED - FOUND: cabana/form_schema.go - FOUND: cabana/schema_types.go - FOUND: cabana/form_schema_test.go - FOUND: internal/build/stubs/artifacts.tmpl - FOUND: ea3f070 - FOUND: 4c814e5 - FOUND: ad1b760 - FOUND: da8828e - FOUND: af8e58a - FOUND: 68715fc --- *Phase: 09-backend-admin-authentication-and-schema-pipeline* *Completed: 2026-09-24*