From bd4960401df99db7e1e4bb43ba1ab9edcde6f658 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Mon, 5 Oct 2026 12:43:23 +0200 Subject: [PATCH] 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 --- .planning/WINDOWS.md | 12 +- .../12.1-02-SUMMARY.md | 449 ++++++++++++++++++ .../deferred-items.md | 11 + 3 files changed, 466 insertions(+), 6 deletions(-) create mode 100644 .planning/phases/12.1-user-plugin-admin-screens/12.1-02-SUMMARY.md create mode 100644 .planning/phases/12.1-user-plugin-admin-screens/deferred-items.md diff --git a/.planning/WINDOWS.md b/.planning/WINDOWS.md index 1ca5074..64a51dd 100644 --- a/.planning/WINDOWS.md +++ b/.planning/WINDOWS.md @@ -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" } ] diff --git a/.planning/phases/12.1-user-plugin-admin-screens/12.1-02-SUMMARY.md b/.planning/phases/12.1-user-plugin-admin-screens/12.1-02-SUMMARY.md new file mode 100644 index 0000000..747a860 --- /dev/null +++ b/.planning/phases/12.1-user-plugin-admin-screens/12.1-02-SUMMARY.md @@ -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* diff --git a/.planning/phases/12.1-user-plugin-admin-screens/deferred-items.md b/.planning/phases/12.1-user-plugin-admin-screens/deferred-items.md new file mode 100644 index 0000000..e7fa670 --- /dev/null +++ b/.planning/phases/12.1-user-plugin-admin-screens/deferred-items.md @@ -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.