docs(12.1-02): complete framework preview and form seams plan

- summary with the v0.1.3 tag, gate times and contract names
- deferred item for two application inventory tests
- WINDOWS entry 9 fixed: RecordActions.vue is mounted
This commit is contained in:
Jakub Zych
2026-10-05 12:43:23 +02:00
parent df5cace852
commit bd4960401d
3 changed files with 466 additions and 6 deletions

View File

@@ -1,10 +1,10 @@
---
schema_version: 1
open_count: 5
open_count: 4
waived_count: 0
fixed_count: 4
fixed_count: 5
total_count: 9
last_updated: 2026-10-04T21:55:30.405Z
last_updated: 2026-10-05T10:41:16.319Z
---
# Broken Windows Ledger
@@ -23,7 +23,7 @@ last_updated: 2026-10-04T21:55:30.405Z
| 6 | 10.1 | stub | plugins/golem15/fonoteka/controllers/albums_admin_controller.go | 71 | fonoteka.go: discogsLookup widget action is a D-02 stub answering fixed fill {year: 1977, format: LP}; Phase 14 replaces it with the Discogs client | open | | 2026-09-29T00:27:32.945Z | |
| 7 | 10.1 | stub | plugins/golem15/fonoteka/controllers/albums_admin_controller.go | 82 | fonoteka.go: discogsSync toolbar action is a D-02 stub answering stub_not_implemented and changing nothing; Phase 14 replaces it with the Discogs sync | open | | 2026-09-29T00:27:33.153Z | |
| 8 | 11 | skipped-test | parity/broadcast_goldens_test.go | | fonoteka.go: TestBroadcastGoldens/created and /updated t.Skip as pending; Phase 12 turns the recorded PHP created/updated goldens into assertions (album subtree, literal created_at/updated_at and artist id handling) | open | | 2026-09-30T11:32:37.302Z | |
| 9 | 12.1 | stub | admin/src/components/form/RecordActions.vue | | RecordActions.vue is built and tested but not mounted; plan 12.1-02 mounts it in the preview footer, until then meta.actions is served and no screen shows the buttons | open | | 2026-10-04T21:55:30.405Z | |
| 9 | 12.1 | stub | admin/src/components/form/RecordActions.vue | | RecordActions.vue is built and tested but not mounted; plan 12.1-02 mounts it in the preview footer, until then meta.actions is served and no screen shows the buttons | fixed | | 2026-10-04T21:55:30.405Z | 2026-10-05T10:41:16.319Z |
````json
[
@@ -138,10 +138,10 @@ last_updated: 2026-10-04T21:55:30.405Z
"file": "admin/src/components/form/RecordActions.vue",
"line": null,
"description": "RecordActions.vue is built and tested but not mounted; plan 12.1-02 mounts it in the preview footer, until then meta.actions is served and no screen shows the buttons",
"status": "open",
"status": "fixed",
"reason": "",
"recorded_at": "2026-10-04T21:55:30.405Z",
"resolved_at": null,
"resolved_at": "2026-10-05T10:41:16.319Z",
"milestone": "v1.0"
}
]

View File

@@ -0,0 +1,449 @@
---
phase: 12.1-user-plugin-admin-screens
plan: 02
subsystem: admin
tags: [cabana, pact, admin-spa, preview, permissioneditor, password, virtual-fields, relation-lock, vue, openapi, release]
requires:
- phase: 12.1-user-plugin-admin-screens
provides: "plan 01: record actions with meta.actions, cabana.ForbiddenError, RecordActions.vue, the acme.roster fixture"
- phase: 12.2-admin-form-fields
provides: lagoon.Transaction write paths, datepicker and fileupload read-only modes, tags v0.1.1 and v0.1.2
provides:
- "config_form.yaml preview block, context: preview, the SPA preview route and PreviewView.vue"
- "pact.FormVirtualFields, pact.FormRules, cabana.VirtualFieldsFromContext"
- "fields.yaml types password and permissioneditor, key preset"
- "cabana.PermissionOption, cabana.PermissionEditorProvider"
- "cabana.FieldRelationContract.WritableForeignKey, cabana.RelationLock, cabana.RelationLockProvider, RelationOption.Locked"
- "columns.yaml invisible; pact.FilterOptions asked on the controller before the model"
- "annotated tag v0.1.3 on df5cace (local; push pending)"
affects: [12.1-03, 12.1-04, 12.1-05, sm-user-plugin admin controllers, fonoteka.go admin inventory tests]
actuals:
tokens: 362238 # chars/4 over the whole realized diff; 83998 without dist, admin.json and schema.d.ts
tasks: 6
commits: 4
plan_head_before: 1c99de50134d5f53248c91f4b234770154a2ccdb
plan_head_after: df5cace8525bc10fa40b25552137290793c08bb3
tech-stack:
added: []
patterns:
- "One file per field type (field_permission.go beside field_date.go and field_file.go)"
- "Per-request schema parts (permission options) are set on the localized copy, never on the cached schema"
- "Form-only values travel to hooks on the transaction context, never through Fill or the projection"
- "Locks are enforced inside the save transaction before any row write; the flag on options is display only"
key-files:
created:
- modules/cabana/field_permission.go
- modules/cabana/phase121_form_test.go
- modules/cabana/example_form_seams_test.go
- modules/cabana/testdata/roster/controllers/people/_status.htm
- modules/cabana/testdata/roster/controllers/people/config_filter.yaml
- admin/src/views/PreviewView.vue
- admin/src/components/form/PreviewField.vue
- admin/src/components/form/fields/PasswordField.vue
- admin/src/components/form/fields/PermissionEditorField.vue
- admin/tests/smoke/preview.smoke.test.ts
- admin/tests/smoke/seams.smoke.test.ts
- admin/tests/fixtures/roster.form-schema.json
modified:
- modules/pact/capabilities.go
- modules/cabana/form_schema.go
- modules/cabana/schema_types.go
- modules/cabana/crud.go
- modules/cabana/tx_context.go
- modules/cabana/extension.go
- modules/cabana/relation_field.go
- modules/cabana/list_schema.go
- modules/cabana/filter_schema.go
- modules/cabana/http.go
- modules/cabana/messages.go
- modules/cabana/settings.go
- modules/cabana/relation_form.go
- admin/src/app/router.ts
- admin/src/app/winterUrl.ts
- admin/src/views/FormView.vue
- admin/src/components/form/formState.ts
- admin/src/components/form/control.ts
- admin/src/components/form/fields/RelationField.vue
- admin/src/components/list/DataTable.vue
- admin/src/styles/main.css
key-decisions:
- "Task 5 answer: tag-local. v0.1.3 is an annotated local tag on df5cace; master and the tag are not pushed"
- "password, permissioneditor and preset are refused on settings forms and on relation (manage and pivot) forms"
- "The SPA sends a permissioneditor field on every update, reduced to the offered codes"
- "Relation locks are enforced on form create and update only; relation-manager link and unlink routes do not ask the lock provider"
- "pact.FilterOptions keeps its signature (no context parameter); the controller is asked before the model"
- "A null preview: is detected by checking the document's root keys, because goccy does not call a custom unmarshaler for null"
patterns-established:
- "A form that declares recordActions must declare preview:; write preview: {} for a screen without a status hint"
- "A type: password field must be listed in the controller's FormVirtualFields"
- "A new required message key needs the typed SPA fixtures and the conformance fixture updated in the same commit"
requirements-completed: [SC-1, SC-2, SC-3, SC-4] # copied from the plan; these are phase success criteria shared with plans 03 to 05, which finish them
coverage:
- id: D1
description: "Preview screen: preview block, context preview, status hint partial, record actions and the edit button in the footer, preview redirects"
requirement: SC-1
verification:
- kind: integration
ref: "modules/cabana/phase121_form_test.go#TestPreviewSmoke"
status: pass
- kind: automated_ui
ref: "admin/tests/smoke/preview.smoke.test.ts#preview screen (UI-SPEC S3, D-11)"
status: pass
- kind: automated_ui
ref: "admin/tests/smoke/preview.smoke.test.ts#preview footer (UI-SPEC S2, D-10)"
status: pass
- kind: automated_ui
ref: "admin/tests/smoke/preview.smoke.test.ts#preview URLs (T-12.1-16)"
status: pass
- kind: manual_procedural
ref: "Task 1 tracer checkpoint: the user opened the preview on the demo server and approved it"
status: pass
human_judgment: false
- id: D2
description: "Password field with confirmation and form-only (virtual) fields that reach hooks and are never bound, filled or returned"
requirement: SC-3
verification:
- kind: integration
ref: "modules/cabana/phase121_form_test.go#TestPasswordFieldSmoke"
status: pass
- kind: integration
ref: "modules/cabana/phase121_form_test.go#TestVirtualFieldsSmoke"
status: pass
- kind: automated_ui
ref: "admin/tests/smoke/seams.smoke.test.ts#password field (UI-SPEC S7, D-19)"
status: pass
human_judgment: false
- id: D3
description: "Rules per operation from the controller (pact.FormRules) and the preset key on text fields"
requirement: SC-3
verification:
- kind: integration
ref: "modules/cabana/phase121_form_test.go#TestFormRulesSmoke"
status: pass
- kind: unit
ref: "modules/cabana/phase121_form_test.go#TestPresetSchema"
status: pass
- kind: automated_ui
ref: "admin/tests/smoke/seams.smoke.test.ts#preset (D-27 G7)"
status: pass
human_judgment: false
- id: D4
description: "permissioneditor field in radio or checkbox mode: per-request options, 422 for unknown codes and values, 403 for a changed locked code, kept unknown stored codes"
requirement: SC-2
verification:
- kind: integration
ref: "modules/cabana/phase121_form_test.go#TestPermissionEditorSmoke"
status: pass
- kind: automated_ui
ref: "admin/tests/smoke/seams.smoke.test.ts#permission editor (UI-SPEC S5, D-16)"
status: pass
human_judgment: true
rationale: "Section layout, the three-segment control, wrapping below 640px and dark mode are visual; the checkbox-mode value set has no HTTP test yet (plan 05)."
- id: D5
description: "Writable protected foreign key through a relation field, and locked relation options enforced with 403 on create and update"
requirement: SC-3
verification:
- kind: integration
ref: "modules/cabana/phase121_form_test.go#TestWritableForeignKeySmoke"
status: pass
- kind: integration
ref: "modules/cabana/phase121_form_test.go#TestRelationLockSmoke"
status: pass
- kind: automated_ui
ref: "admin/tests/smoke/seams.smoke.test.ts#locked relation options (UI-SPEC S6, D-07)"
status: pass
human_judgment: true
rationale: "Relation-manager link and unlink routes do not ask the lock provider; whether that is acceptable for T-12-18 is a review decision for plans 04 and 05."
- id: D6
description: "Invisible list columns (searched, not sent, not rendered) and filter choices served by the controller"
requirement: SC-1
verification:
- kind: integration
ref: "modules/cabana/phase121_form_test.go#TestInvisibleColumnSmoke"
status: pass
- kind: integration
ref: "modules/cabana/phase121_form_test.go#TestFilterOptionsController"
status: pass
- kind: automated_ui
ref: "admin/tests/smoke/seams.smoke.test.ts#invisible list column (D-27 G6)"
status: pass
human_judgment: false
- id: D7
description: "Framework released as the annotated tag v0.1.3 on a head where every plan gate passed; push pending by the user's choice"
requirement: SC-4
verification:
- kind: other
ref: "git cat-file -t v0.1.3 = tag; git rev-parse 'v0.1.3^{commit}' = df5cace8525bc10fa40b25552137290793c08bb3"
status: pass
- kind: other
ref: "Task 6 gate table in this summary"
status: pass
human_judgment: true
rationale: "The push of master and v0.1.3 is a pending release step, and two application inventory tests outside the plan's gate fail against this tree (see Issues Encountered)."
duration: 12h 43m
completed: 2026-10-05
status: complete
---
# Phase 12.1 Plan 02: Framework preview and form seams Summary
**Any plugin form can now have a read-only preview screen with a status hint and record actions, a password with confirmation, form-only fields that reach hooks, its own admin rules per operation, a permission editor, a writable foreign-key picker, locked relation options the server enforces, preset fields and hidden search columns. The framework is tagged v0.1.3 locally on `df5cace`; nothing was pushed.**
## Performance
- **Duration:** 12h 43m wall clock, including two waits for the user (the Task 1 tracer checkpoint and the Task 5 decision) and an overnight pause. Task 6 itself took about 8 minutes.
- **Started:** 2026-10-04T21:57:49Z
- **Completed:** 2026-10-05T10:41:16Z
- **Tasks:** 6 of 6 (four code tasks, the decision checkpoint, the release task)
- **Files modified:** 77 source files, plus the rebuilt `modules/boardwalk/dist` (82 paths in total)
## Accomplishments
- A list row opens a preview screen: the record as a read-only `dl` grid, a status hint partial above the card, the record actions of plan 01 and one edit button in the footer. `RecordActions.vue` is now mounted, which closes WINDOWS entry 9.
- A form collects a password with confirmation and other form-only values. The framework never binds, fills or returns them; hooks read them with `cabana.VirtualFieldsFromContext`.
- A controller supplies the validation rules of an admin save per operation, replacing the model's `Rules()`.
- A `permissioneditor` field shows the permissions a controller offers, in radio or checkbox mode. The server accepts only offered codes and allowed values and refuses a change to a locked code with 403.
- A relation field can write a protected foreign key when its contract says so, and a controller can lock related ids per admin; a save that changes the locked subset is refused with 403 before any row is written.
- `columns.yaml` accepts `invisible: true`, text fields accept `preset`, and scope-filter choices can come from the controller.
## Release: v0.1.3
| Item | Value |
|------|-------|
| Option chosen at Task 5 | `tag-local` |
| Tag | `v0.1.3`, annotated, message "SummerCMS v0.1.3: bulk and record actions, preview screen, row state, permission editor, form seams" |
| Tagged commit | `df5cace8525bc10fa40b25552137290793c08bb3` |
| On origin | No. `git ls-remote --tags origin v0.1.3` lists nothing |
| Earlier tags | `v0.1.1` (5c38d96) and `v0.1.2` (6525d96) unchanged |
**Pending release steps (not done, by the user's choice):**
1. Push framework `master` and the tag: `git push origin master v0.1.3`. `master` was 10 commits ahead of `origin/master` at the tag, plus the planning commits made after it.
2. The sm-user-plugin push of plan 05 Task 3 waits for step 1. It runs only when `git ls-remote --tags origin v0.1.3` lists the tag, because a published plugin commit must not depend on an unpublished framework contract.
Until the push, the local tag can still be moved if a name has to change.
### Gates on the tagged head (measured, for VALIDATION.md)
All ran on `df5cace` with a clean tree (only the three untracked files that predate this plan).
| Gate | Result | Time |
|------|--------|------|
| `go vet ./...` | pass | under 1 s (build cache warm) |
| `go test ./... -count=1` | pass, no `FAIL` line | 52 s |
| `go test ./modules/cabana -run '^(TestPhase09ContractInventory\|TestPhase09PermissionMatrix\|TestPhase10OpenAPIConformance)$' -count=1 -v` | 3 of 3 pass | 16 s |
| `npm --prefix admin run typecheck` | pass | 10 s |
| `npm --prefix admin test` | 63 files, 848 tests pass | 26 s |
| `scripts/check-admin-openapi.sh --check` | clean | 2 s |
| `scripts/check-admin-dist.sh` | "modules/boardwalk/dist matches a fresh build" | 16 s |
| `go test ./cmd/summer -run TestDocsTree -count=1` | pass | 4 s |
| `go run ./cmd/summer docs:build --check` | "no problems found" | 2 s |
| `go -C ../fonoteka.go build ./...` | pass | 3 s |
| `go -C ../fonoteka.go vet ./...` | pass | under 1 s |
| `go -C ../fonoteka.go test ./... -count=1` | pass (root module: `fonoteka`, `fonoteka/parity`) | 69 s |
Earlier rough numbers from Tasks 1 to 4: `go test ./modules/cabana/... -count=1` about 33 s, `npm --prefix admin test` about 22 s.
Extra runs, not part of the plan's gate (the application's go.work plugin modules, which `./...` in the root module does not match):
| Module | vet | test |
|--------|-----|------|
| `plugins/golem15/user` | pass | pass, 18 s |
| `plugins/golem15/golem` | pass | pass, 8 s |
| `plugins/golem15/feedback` | pass | pass, 8 s |
| `plugins/golem15/fonoteka` | pass | **2 tests fail**, 98 s (see Issues Encountered) |
## Contract names (inputs to plans 03 to 05)
Every name below was checked with `go doc` at the tagged commit.
| Name | Shape |
|------|-------|
| `pact.FormVirtualFields` | `FormVirtualFields() []string` |
| `pact.FormRules` | `FormRules(ctx context.Context, op string) map[string]string`; op is `create` or `update` |
| `pact.FilterOptions` | unchanged: `FilterOptions(scope string) []Option`; the admin controller is asked before the model |
| `cabana.VirtualFieldsFromContext` | `(ctx context.Context) (map[string]any, bool)`; a copy; false outside a create or update save |
| `cabana.FormPreview` | `HeaderPartial string` (json `headerPartial`, omitempty) |
| `cabana.FormView.Preview` | `*FormPreview` (json `preview`, omitempty) |
| `cabana.FormMessages.Preview`, `.Edit` | YAML and JSON keys `preview`, `edit` |
| `cabana.FieldPreset` | `Field` (json `field`), `Type` (json `type`: `slug` or `exact`) |
| `cabana.FormField.Preset` | `*FieldPreset` |
| `cabana.PermissionOption` | `Code, Label, Tab, Comment string; Locked bool` (json `code`, `label`, `tab`, `comment`, `locked`; the last three omitempty) |
| `cabana.FormField.PermissionOptions` | `[]PermissionOption` (json `permissionOptions`, omitempty) |
| `cabana.PermissionEditorProvider` | `AdminPermissionOptions(ctx, field string) ([]PermissionOption, error)`; `AdminPermissionValues(ctx, field string, record any) (map[string]int, error)`; `AdminSetPermissionValues(ctx, field string, record any, values map[string]int) error` |
| `cabana.FieldRelationContract.WritableForeignKey` | `bool`; belongsTo only |
| `cabana.RelationLock` | `IDs []uint; Message string` |
| `cabana.RelationLockProvider` | `AdminRelationLocks(ctx context.Context, field string) (RelationLock, error)` |
| `cabana.RelationOption.Locked` | `bool` (json `locked`, omitempty) |
| `cabana.ListColumn.Invisible` | `bool` (json `invisible`, omitempty) |
| TS aliases | `FormPreview`, `FieldPreset`, `PermissionOption` |
The tag also publishes the plan 01 names (`pact.AdminBulkAction`, `HasAdminBulkActions`, `AdminRecordAction`, `HasAdminRecordActions`, `RowState`, `ListRowStates`; `cabana.ForbiddenError`, `BulkActionResult`, `RecordAction`); see 12.1-01-SUMMARY.md.
YAML: `config_form.yaml` `preview:` (a mapping with the optional key `headerPartial`; `preview: {}` enables the screen without a hint; a null value stops boot) and `messages.preview`, `messages.edit`; `fields.yaml` types `password` and `permissioneditor`, `mode: radio|checkbox` on `permissioneditor`, `preset` on text fields (a field name, or `field` plus `type`); `columns.yaml` `invisible`.
SPA: route `preview` at `/:vendor/:plugin/:controller/:id/preview`; `PreviewView.vue`, `PreviewField.vue`, `PasswordField.vue`, `PermissionEditorField.vue`; `FormMode` has `preview`; `mapWinterUrl` maps `preview/:id`; `PartialHost.vue` has a `hint` prop; style kit classes `.summer-callout`, `.summer-callout--warning`, `.summer-callout--danger`, `.summer-callout__title`, `.summer-callout__text`.
Phrase keys added in en and pl: `backend::lang.form.{return_to_preview, locked_item, locked_note, show_password, hide_password}`, `backend::lang.permissioneditor.{allow, inherit, deny, locked, empty, other}`, `backend::lang.messages.form.{preview, edit}`.
Fixture: `acme.roster` gained the preview block with the `status` partial, the fields `joined_ip`, `password`, `password_confirmation`, `notify`, `slug`, `permissions`, `team`, `tags`, an invisible `email` column, a visible `slug` column and the `tagged` filter. Helpers: `newRosterEnvWith`, `rosterBootWith`; `rosterPlugin` has `db` and `relations`.
### Known contract properties
The user was shown these six before choosing `tag-local` and accepted them.
1. Settings forms and relation (manage and pivot) forms refuse `password`, `permissioneditor` and `preset` at boot.
2. Virtual values reach hooks as decoded from JSON: a string, a bool, a `json.Number` or nil. A number is a `json.Number`, not an int or float.
3. The SPA sends a `permissioneditor` field on every update, reduced to the offered codes.
4. Relation locks are enforced on form create and update only. The relation-manager link and unlink routes do not ask `RelationLockProvider`.
5. `pact.FilterOptions` has no context parameter, so a controller serving choices uses the database handle it holds, without the request's principal.
6. A locked permission code whose stored value is outside the mode's set makes every save of that record a 403, because the SPA cannot send the stored value back.
## Task Commits
1. **Task 1: read-only preview screen (tracer)** - `a65c670` (feat). The user approved the screen at the tracer checkpoint.
2. **Task 2: password and form-only fields, rules per operation, preset** - `a1c6bb1` (feat)
3. **Task 3: permissioneditor field** - `f50d9b8` (feat)
4. **Task 4: writable foreign keys, locked relation options, invisible columns, controller filter choices** - `df5cace` (feat)
5. **Task 5: checkpoint:decision** - answered `tag-local`; no commit
6. **Task 6: gates and tag** - no source change; annotated tag `v0.1.3` on `df5cace`
## Decisions Made
- `tag-local` at Task 5 (the user's decision): the tag exists locally and the push is a pending release step.
- `password`, `permissioneditor` and `preset` are refused outside ordinary controller forms, because settings and relation forms have no hook path that could consume them safely.
- The null `preview:` check reads the document's root keys, since the YAML library does not call a custom unmarshaler for a null value.
- `PreviewView.vue` provides a read-only `FORM_SESSION`, so a `fileupload` field can list its files on the preview.
- `editablePayload` reduces a `permissioneditor` value to the offered codes, so codes the server no longer offers are never sent back.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] A null `preview:` was not refused by the decoder alone**
- **Found during:** Task 1
- **Issue:** goccy does not call a custom unmarshaler for null, so `preview:` with no value compiled as "no preview".
- **Fix:** `CompileForm` checks the root keys (`topLevelKey`, using `goccy/go-yaml/parser`, which is already in the module; go.mod unchanged).
- **Committed in:** a65c670
**2. [Rule 3 - Blocking] Fixtures and tests for the two new required form messages and the boot rule**
- **Found during:** Task 1
- **Issue:** `FormMessages` gained two required keys, and `recordActions` now needs `preview`.
- **Fix:** four typed SPA form fixtures got `preview` and `edit`; the conformance fixture and the `unregistered` case of `TestFormSchemaRecordActionsBoot` got `preview: {}`; `winterUrl.test.ts`, `edit.smoke.test.ts` and `actions.smoke.test.ts` were updated (outside `files_modified`).
- **Committed in:** a65c670
**3. [Rule 3 - Blocking] Existing creates needed a password; the fixture stamps the tenant**
- **Found during:** Task 2
- **Issue:** the fixture's `FormRules` makes a password required on create, which broke two existing creates.
- **Fix:** `phase121_actions_test.go` and `TestPreviewSmoke` send a password pair; the fixture's `FormBeforeCreate` stamps the tenant.
- **Committed in:** a1c6bb1
**4. [Rule 2 - Missing critical] `password` and `preset` refused on settings and relation forms**
- **Found during:** Task 2
- **Issue:** these forms have no virtual-field path, so a password there would have been treated as a column.
- **Fix:** boot errors in `settings.go` and `relation_form.go` (outside `files_modified`); `mergedRules` gained a `ctx` parameter (`relation.go`, `relation_child.go`).
- **Committed in:** a1c6bb1
**5. [Rule 3 - Blocking] Docs fences on methods need a whole example file**
- **Found during:** Task 2
- **Fix:** `example_form_seams_test.go` has `Example_formSeams`, so the `src=` fences resolve.
- **Committed in:** a1c6bb1
**6. [Rule 3 - Blocking] Datepicker tests expected the old `mode` message**
- **Found during:** Task 3
- **Fix:** `datepicker_test.go` and `datepicker_smoke_test.go` updated for "mode is only valid on type: fileupload, datepicker or permissioneditor".
- **Committed in:** f50d9b8
**7. [Rule 1 - Bug] The SPA could send permission codes that are no longer offered**
- **Found during:** Task 3
- **Fix:** `editablePayload` reduces the value through `permissionValues` in `admin/src/components/form/control.ts` (outside the task's file list).
- **Committed in:** f50d9b8
**8. [Rule 2 - Missing critical] `permissioneditor` refused on settings and relation forms**
- **Found during:** Task 3
- **Committed in:** f50d9b8
**9. [Rule 3 - Blocking] An invisible `email` column left the roster list with one visible column**
- **Found during:** Task 4
- **Issue:** a plan 01 row-state assertion needs a second visible column.
- **Fix:** `columns.yaml` and the SPA list fixtures gained a visible `slug` column.
- **Committed in:** df5cace
### Other departures from the plan text
- **Task 1:** `PartialHost.vue` gained a `hint` prop for the 68px skeleton and the keep-previous behaviour.
- **Task 2:** `preview.smoke.test.ts` lists the new `slug` field in its `dt` check; `editablePayload` gained an optional `mode` argument.
- **Task 3:** `PreviewField.vue` needed no change for the permission editor; the provider-missing boot test uses the conformance fixture.
- **Task 4:** `summer docs:sync` rewrote the `config_list.yaml` fence in `docs/backend/admin-controllers.md`; `query.go` needed no change; the SPA list-schema fixture keeps `filters: []`; the new file `testdata/roster/controllers/people/config_filter.yaml` and the helpers `newRosterEnvWith` and `rosterBootWith` were added.
- **Task 6:** no source change. The plugin-module runs in the gate section are an addition to the plan's gate.
---
**Total deviations:** 9 auto-fixed (2 bugs, 2 missing critical, 5 blocking) and the departures listed above.
**Impact on plan:** No scope added. Every extra file is a test, fixture or example the new keys require, or a refusal that keeps a new field type off forms that cannot handle it.
## Issues Encountered
**Two application tests fail against the tagged framework.** They are outside the plan's gate and were found by an extra run in Task 6.
- `go -C ../fonoteka.go/plugins/golem15/fonoteka test ./... -count=1` fails in `TestPhase09SecurityRoutes` and `TestPhase10ControllerCopy`.
- Both compare against fixed lists in the application's tests. `phase09AdminRoutes` does not name the two plan 01 routes (bulk action, record action). `phase10ListMessageKeys` does not name the three plan 01 row-state messages. The form half of `TestPhase10ControllerCopy` was not reached and may need `preview` and `edit` in the same way.
- No framework change is needed, so the tagged commit is unaffected. The fix is in fonoteka.go, which this plan does not write to. The same catch-up happened once before (`549840d` in fonoteka.go, for the Phase 12.2 routes).
- The plan's application gate passed because `go -C ../fonoteka.go test ./...` covers the root module only; the four go.work plugin modules are separate modules.
- Recorded in `deferred-items.md` in this phase directory, with plan 04 or the plan 05 gate script as the suggested owner.
The tag was still created: every gate the plan defines passed on `df5cace`, the failures need no change to that commit, and the tag is local and can be moved.
## Not verified here
- A form-level `required: true` on a virtual field has no test (Task 2).
- The checkbox-mode value set of the permission editor has no HTTP test on the server; radio mode has (Task 3).
- The model-only `FilterOptions` path relies on the existing `TestPhase10FilterOptions`; no new test was added for it.
- Visual checks of the permission editor, the locked chips and the password toggle in a browser. Only the preview screen was looked at by the user.
- `scripts/check-phase10.sh` and the other phase gates were not run; only the commands the plan names.
## Open items for plan 05's review
1. **Relation locks and the relation manager (property 4).** A locked id can still be linked or unlinked through the relation-manager routes, because only form saves call `checkRelationLocks`. Plan 04's privileged-group guard (T-12-18) must cover those routes in the plugin, or the framework needs a follow-up.
2. **Locked permission with a stored value outside the mode's set (property 6).** Every save of such a record is a 403. Decide whether the server should compare a locked code only when it was submitted, or whether the plugin must normalize stored values.
3. **Application inventory tests** (see Issues Encountered).
4. The three untested paths under "Not verified here".
## Known Stubs
None. `RecordActions.vue` is mounted in `PreviewView.vue`, and WINDOWS entry 9 is marked fixed.
The demo fixture used at the tracer checkpoint registers no navigation, so the SPA home shows "No sections are available." and the list was opened by direct URL. This is a property of the test fixture, not a defect.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Plan 12.1-03 has its precondition: `v0.1.3` exists locally, and the application resolves the framework through its local replace.
- No Go module and no npm package changed: `git diff --stat 1c99de5..df5cace -- go.mod go.sum admin/package.json admin/package-lock.json` is empty.
- Pending: push `master` and `v0.1.3`; the sm-user-plugin push of plan 05 waits for it.
- Pending: the two application inventory tests in fonoteka.go.
- A demo server for the user is still running on 127.0.0.1:8431; this plan left it alone.
## Self-Check: PASSED
- Created files exist: `field_permission.go`, `phase121_form_test.go`, `example_form_seams_test.go`, the two new roster fixture files, the four SPA components, the two smoke tests, `roster.form-schema.json`.
- Commits exist: a65c670, a1c6bb1, f50d9b8, df5cace; `git rev-list --count 1c99de5..HEAD` was 4 when this summary was written.
- Tag: `git tag --list v0.1.3` prints `v0.1.3`; `git cat-file -t v0.1.3` prints `tag`; `git rev-parse 'v0.1.3^{commit}'` is `df5cace8525bc10fa40b25552137290793c08bb3`; `git merge-base --is-ancestor v0.1.3 HEAD` succeeds; `v0.1.1` and `v0.1.2` are unchanged.
- Key links: `name: 'preview'` once in `router.ts`; `RecordActions` used in `PreviewView.vue`; the ten `Test*` functions named in the coverage block are in `phase121_form_test.go`.
- Nothing was pushed: `git ls-remote --tags origin v0.1.3` lists nothing.
---
*Phase: 12.1-user-plugin-admin-screens*
*Completed: 2026-10-05*

View File

@@ -0,0 +1,11 @@
# Phase 12.1 deferred items
## Deferred Items
- Two inventory tests of the application's fonoteka plugin module fail against the framework at v0.1.3
status: open
**Found:** plan 12.1-02 Task 6, 2026-10-05, by running `go -C ../fonoteka.go/plugins/golem15/fonoteka test ./... -count=1` in addition to the plan's gate.
**What:** `TestPhase09SecurityRoutes` (`admin_phase09_security_test.go`) reports the two plan 01 routes `POST .../{controller}/bulk/{action}` and `POST .../{controller}/{id}/actions/{action}` as unexpected, because the fixed list `phase09AdminRoutes` does not name them. `TestPhase10ControllerCopy` (`admin_phase10_copy_test.go`) compares the list messages with the fixed list `phase10ListMessageKeys`, which lacks `rowStateDeleted`, `rowStateNegative` and `rowStateDisabled`; its form half was not reached and may need `preview` and `edit` the same way.
**Why not fixed here:** the fix is two fixed lists in the application repository (fonoteka.go); plan 12.1-02 writes to summercms.go only. No framework change is needed, so the tagged commit is not affected. The same catch-up was done once before (`549840d test(13-06): list the Phase 12.2 cabana admin routes in the Phase 9 route inventory`).
**Why the gate missed it:** the plan's application gate `go -C ../fonoteka.go test ./... -count=1` runs the root module only; the go.work plugin modules (`plugins/golem15/{user,fonoteka,golem,feedback}`) are separate modules and are not matched by `./...`.
**Suggested owner:** plan 12.1-04 (it already writes the application's parity allow-list entry and submodule pointer) or plan 12.1-05's gate script, which should run every workspace module.