245 lines
11 KiB
Markdown
245 lines
11 KiB
Markdown
---
|
|
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*
|