- 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
28 KiB
phase, plan, subsystem, tags, requires, provides, affects, actuals, plan_head_before, plan_head_after, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | actuals | plan_head_before | plan_head_after | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | duration | completed | status | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12.1-user-plugin-admin-screens | 02 | admin |
|
|
|
|
|
1c99de5013 |
df5cace852 |
|
|
|
|
|
|
12h 43m | 2026-10-05 | 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
dlgrid, a status hint partial above the card, the record actions of plan 01 and one edit button in the footer.RecordActions.vueis 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
permissioneditorfield 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.yamlacceptsinvisible: true, text fields acceptpreset, 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):
- Push framework
masterand the tag:git push origin master v0.1.3.masterwas 10 commits ahead oforigin/masterat the tag, plus the planning commits made after it. - 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.3lists 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.
- Settings forms and relation (manage and pivot) forms refuse
password,permissioneditorandpresetat boot. - Virtual values reach hooks as decoded from JSON: a string, a bool, a
json.Numberor nil. A number is ajson.Number, not an int or float. - The SPA sends a
permissioneditorfield on every update, reduced to the offered codes. - Relation locks are enforced on form create and update only. The relation-manager link and unlink routes do not ask
RelationLockProvider. pact.FilterOptionshas no context parameter, so a controller serving choices uses the database handle it holds, without the request's principal.- 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
- Task 1: read-only preview screen (tracer) -
a65c670(feat). The user approved the screen at the tracer checkpoint. - Task 2: password and form-only fields, rules per operation, preset -
a1c6bb1(feat) - Task 3: permissioneditor field -
f50d9b8(feat) - Task 4: writable foreign keys, locked relation options, invisible columns, controller filter choices -
df5cace(feat) - Task 5: checkpoint:decision - answered
tag-local; no commit - Task 6: gates and tag - no source change; annotated tag
v0.1.3ondf5cace
Decisions Made
tag-localat Task 5 (the user's decision): the tag exists locally and the push is a pending release step.password,permissioneditorandpresetare 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.vueprovides a read-onlyFORM_SESSION, so afileuploadfield can list its files on the preview.editablePayloadreduces apermissioneditorvalue 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:
CompileFormchecks the root keys (topLevelKey, usinggoccy/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:
FormMessagesgained two required keys, andrecordActionsnow needspreview. - Fix: four typed SPA form fixtures got
previewandedit; the conformance fixture and theunregisteredcase ofTestFormSchemaRecordActionsBootgotpreview: {};winterUrl.test.ts,edit.smoke.test.tsandactions.smoke.test.tswere updated (outsidefiles_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
FormRulesmakes a password required on create, which broke two existing creates. - Fix:
phase121_actions_test.goandTestPreviewSmokesend a password pair; the fixture'sFormBeforeCreatestamps 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.goandrelation_form.go(outsidefiles_modified);mergedRulesgained actxparameter (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.gohasExample_formSeams, so thesrc=fences resolve. - Committed in:
a1c6bb1
6. [Rule 3 - Blocking] Datepicker tests expected the old mode message
- Found during: Task 3
- Fix:
datepicker_test.goanddatepicker_smoke_test.goupdated 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:
editablePayloadreduces the value throughpermissionValuesinadmin/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.yamland the SPA list fixtures gained a visibleslugcolumn. - Committed in:
df5cace
Other departures from the plan text
- Task 1:
PartialHost.vuegained ahintprop for the 68px skeleton and the keep-previous behaviour. - Task 2:
preview.smoke.test.tslists the newslugfield in itsdtcheck;editablePayloadgained an optionalmodeargument. - Task 3:
PreviewField.vueneeded no change for the permission editor; the provider-missing boot test uses the conformance fixture. - Task 4:
summer docs:syncrewrote theconfig_list.yamlfence indocs/backend/admin-controllers.md;query.goneeded no change; the SPA list-schema fixture keepsfilters: []; the new filetestdata/roster/controllers/people/config_filter.yamland the helpersnewRosterEnvWithandrosterBootWithwere 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=1fails inTestPhase09SecurityRoutesandTestPhase10ControllerCopy.- Both compare against fixed lists in the application's tests.
phase09AdminRoutesdoes not name the two plan 01 routes (bulk action, record action).phase10ListMessageKeysdoes not name the three plan 01 row-state messages. The form half ofTestPhase10ControllerCopywas not reached and may needpreviewandeditin 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 (
549840din 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.mdin 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: trueon 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
FilterOptionspath relies on the existingTestPhase10FilterOptions; 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.shand the other phase gates were not run; only the commands the plan names.
Open items for plan 05's review
- 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. - 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.
- Application inventory tests (see Issues Encountered).
- 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.3exists 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.jsonis empty. - Pending: push
masterandv0.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..HEADwas 4 when this summary was written. - Tag:
git tag --list v0.1.3printsv0.1.3;git cat-file -t v0.1.3printstag;git rev-parse 'v0.1.3^{commit}'isdf5cace8525bc10fa40b25552137290793c08bb3;git merge-base --is-ancestor v0.1.3 HEADsucceeds;v0.1.1andv0.1.2are unchanged. - Key links:
name: 'preview'once inrouter.ts;RecordActionsused inPreviewView.vue; the tenTest*functions named in the coverage block are inphase121_form_test.go. - Nothing was pushed:
git ls-remote --tags origin v0.1.3lists nothing.
Phase: 12.1-user-plugin-admin-screens Completed: 2026-10-05