docs(09-03): complete typed winter form schema plan
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
---
|
||||
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/<name>/config_form.yaml and config_list.yaml pointing at models/<name>/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/<name>/config_form.yaml` and `config_list.yaml` that point at `models/<name>/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/<name>`.
|
||||
|
||||
## 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*
|
||||
Reference in New Issue
Block a user