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:
@@ -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"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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*
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user