docs(18-01): complete framework admin pieces plan
This commit is contained in:
@@ -1113,11 +1113,11 @@ Plans:
|
||||
4. A host test proves the order-create, Stripe-pay and order-paid flow that Horoskopia relies on.
|
||||
5. The new code has unit tests, delivered in the phase's last plan.
|
||||
|
||||
**Plans:** 7 plans
|
||||
**Plans:** 1/7 plans executed
|
||||
|
||||
Plans:
|
||||
**Wave 1**
|
||||
- [ ] 18-01-PLAN.md — Framework (summercms.go): cabana `money` field and list column (D-07, scale-4 decimal strings), write-only `lagoon.Encrypted` secrets, richeditor and tabs on settings forms (D-13, F2-F4), danger record actions, preview Edit rule and the table kit (F5-F7), docs, OpenAPI, dist; framework tag with go-ahead
|
||||
- [x] 18-01-PLAN.md — Framework (summercms.go): cabana `money` field and list column (D-07, scale-4 decimal strings), write-only `lagoon.Encrypted` secrets, richeditor and tabs on settings forms (D-13, F2-F4), danger record actions, preview Edit rule and the table kit (F5-F7), docs, OpenAPI, dist; framework tag with go-ahead
|
||||
|
||||
**Wave 2** *(blocked on 18-01)*
|
||||
- [ ] 18-02-PLAN.md — sm-paymentgateway-plugin domain core: v2 row-shape gate, exact money, migrations, models, FSMs, Order/Payment services, operators (cash, manual, disabled) and registry, cart with legacy reader and extension registry, hashids, events and bridge, notification route, currencies (NBP, Fixer), `pg:*` commands, `paymentgateway:import-winter`
|
||||
@@ -1340,7 +1340,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
|
||||
| 15. Journal plugin | 4/4 | Complete | 2026-10-06 |
|
||||
| 16. grzybyfunkcjonalne.pl on reusable blog views | 5/5 | In Progress| |
|
||||
| 17. Blog companions | 6/6 | In Progress| |
|
||||
| 18. Payment Gateway | 0/TBD | Not started | - |
|
||||
| 18. Payment Gateway | 1/7 | In Progress| |
|
||||
| 18.1. Payment Gateway Subscriptions | 0/TBD | Not started | - |
|
||||
| 19. Chat and ChatChannels | 0/TBD | Not started | - |
|
||||
| 19.1. Chat satellites | 0/TBD | Not started | - |
|
||||
|
||||
@@ -4,16 +4,16 @@ milestone: v1.0
|
||||
current_phase: 18
|
||||
current_phase_name: Payment Gateway
|
||||
status: executing
|
||||
stopped_at: Phase 18 UI-SPEC approved
|
||||
last_updated: "2026-10-13T03:17:07.125Z"
|
||||
last_activity: 2026-10-12
|
||||
last_activity_desc: "Completed quick task 261012-s3g: Phase 10 security blockers (DOMPurify, check-phase10 hygiene)"
|
||||
state_head: 79b1f855646abf44e470a6f9ce1a1a1ae6376d04
|
||||
stopped_at: Completed 18-01-PLAN.md
|
||||
last_updated: "2026-10-13T09:49:54.928Z"
|
||||
last_activity: 2026-10-13
|
||||
last_activity_desc: Phase 18 execution started
|
||||
state_head: 1e841b4281c0e4280ae5af2f5fed338242f0f7d6
|
||||
progress:
|
||||
total_phases: 40
|
||||
completed_phases: 14
|
||||
total_plans: 175
|
||||
completed_plans: 168
|
||||
completed_plans: 169
|
||||
gsd_state_version: "1.0"
|
||||
milestone_name: milestone
|
||||
---
|
||||
@@ -25,14 +25,14 @@ milestone_name: milestone
|
||||
See: .planning/PROJECT.md (updated 2026-10-12)
|
||||
|
||||
**Core value:** An existing WinterCMS-shaped app can be ported plugin by plugin to a single Go binary without its frontend noticing: the PHP version's API contract is the acceptance test.
|
||||
**Current focus:** Phase 17 — Blog companions
|
||||
**Current focus:** Phase 18 — Payment Gateway
|
||||
|
||||
## Current Position
|
||||
|
||||
Phase: 18 (Payment Gateway) — READY TO EXECUTE
|
||||
Plan: 6 of 6
|
||||
Phase: 18 (Payment Gateway) — EXECUTING
|
||||
Plan: 2 of 7
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-10-12 — Completed quick task 261012-s3g: Phase 10 security blockers (DOMPurify, check-phase10 hygiene)
|
||||
Last activity: 2026-10-13 — Phase 18 execution started
|
||||
|
||||
Progress: [████░░░░░░] 44%
|
||||
|
||||
@@ -225,6 +225,7 @@ Progress: [████░░░░░░] 44%
|
||||
| Phase 17 P04 | 32min | 5 tasks | 70 files |
|
||||
| Phase 17 P05 | 93min | 5 tasks | 103 files |
|
||||
| Phase 17 P06 | 53 min | 4 tasks | 50 files |
|
||||
| Phase 18 P01 | 43min | 4 tasks | 59 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -743,6 +744,9 @@ Recent decisions affecting current work:
|
||||
- [Phase 17]: 17-06: cabana singleton route reruns its transaction once on a Postgres unique violation from SingletonRecord (first-open race); fix 6312929 needs the next framework tag
|
||||
- [Phase 17]: 17-06: scripts/check-phase17.sh is the Phase 17 gate; every Phase 17 test name sits in one stage list (go, plugins, admin, layers, postgres)
|
||||
- [Phase 17]: 17-06: protocol-relative menu URLs stay anchors (D-16 rule) in the server and layer; the plan's text-without-href wording is a deferred question
|
||||
- [Phase 18]: Phase 18 plan 01 release gate: framework tag v0.1.25 held until the phase gate after plan 07 (human reply: hold); nothing pushed or tagged, plans 02 to 06 build against the checkout through go.work replace — One tag for the whole phase with the unit tests of plan 07 included; the plugin plans use go.work replace and need no published version, so holding costs only a later bump
|
||||
- [Phase 18]: Money is a decimal string on the wire and in the SPA (never a float, never rounded or truncated); MoneyField emits the raw compact text for a malformed or too-precise draft so the server 422 stays the gate — D-07 and D-10 forbid rounding and truncation; emitting the last valid prefix on a bad draft would let Save write a truncated amount
|
||||
- [Phase 18]: Settings secrets are write-only: the GET omits the value and reports meta.secrets.<field> as stored, env or empty; on PUT an absent key or empty string keeps the stored value and JSON null clears it; meta is omitted when there is nothing to report so existing payloads stay byte-identical — The value must never leave the server (D-13, T-18-03); omitempty on meta and danger keeps the Phase 10 conformance fixtures and the PHP-shaped payloads unchanged
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -821,6 +825,6 @@ Items acknowledged and carried forward from previous milestone close:
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-10-12T20:45:53.030Z
|
||||
Stopped at: Phase 18 UI-SPEC approved
|
||||
Resume file: .planning/phases/18-payment-gateway/18-UI-SPEC.md
|
||||
Last session: 2026-10-13T09:49:33.284Z
|
||||
Stopped at: Completed 18-01-PLAN.md
|
||||
Resume file: None
|
||||
|
||||
370
.planning/phases/18-payment-gateway/18-01-SUMMARY.md
Normal file
370
.planning/phases/18-payment-gateway/18-01-SUMMARY.md
Normal file
@@ -0,0 +1,370 @@
|
||||
---
|
||||
phase: 18-payment-gateway
|
||||
plan: 01
|
||||
subsystem: admin
|
||||
tags: [cabana, pact, money, settings, secrets, richeditor, tabs, record-actions, preview, boardwalk, release]
|
||||
|
||||
requires:
|
||||
- phase: 17-01
|
||||
provides: cabana richeditor field type and the seaglass sanitizer, settings forms, relation forms
|
||||
- phase: 17-02
|
||||
provides: RichEditorField, FormTabs, settings index, framework v0.1.23 admin SPA baseline
|
||||
- phase: 16.2
|
||||
provides: admin form framework (FormView, PreviewView, field registry, formState, meta.notices)
|
||||
provides:
|
||||
- cabana `money` form field (currency, currencyFrom, scale 2..4, max) with the decimal-string contract and the 422 keys money.invalid, money.too_precise, money.too_large
|
||||
- cabana `money` list column (currency, currencyFrom) with the currencyFrom column carried on every row
|
||||
- settings forms accept `password` on lagoon.Encrypted columns, write-only, with meta.secrets stored/env/empty, keep/clear semantics and cabana.SecretFallbacks
|
||||
- settings forms accept `richeditor` (sanitized through seaglass, removals as meta.notices) and `tab`
|
||||
- pact.AdminRecordAction.Danger (wire: danger), RecordActions confirm dialog danger variant
|
||||
- PreviewView Edit rule (no Edit and no primary fill when the form holds only partial fields)
|
||||
- partial style kit (.summer-table-wrap, .summer-table, th, td, __num, __sub, tfoot td, .summer-stat__note)
|
||||
- MoneyField.vue, admin/src/app/money.ts (parseMoneyInput, formatMoney, trimMoneyDisplay, decimalSeparator)
|
||||
- phrase keys money.* and secret.* (en, pl), regenerated OpenAPI and schema.d.ts, rebuilt modules/boardwalk/dist, docs
|
||||
affects: [18-02 paymentgateway core, 18-03 order admin, 18-04 payment admin, 18-05 stripe operator, 18-06 site integration, 18-07 unit tests and phase-gate tag]
|
||||
|
||||
actuals:
|
||||
tokens: 50846
|
||||
tasks: 4
|
||||
commits: 3
|
||||
plan_head_before: 99ccae232c46b643657126ec7cdff2f4f2e85a0e
|
||||
plan_head_after: 1e841b4281c0e4280ae5af2f5fed338242f0f7d6
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Money is a decimal string end to end: the SPA parses typed text into the canonical dot string without a number, Intl.NumberFormat receives the string, the server validates with scale and max and never rounds or truncates"
|
||||
- "Untouched rule for money: a loaded value is re-submitted byte-identical because the field emits nothing until the user edits"
|
||||
- "Write-only secrets: the settings GET omits the value and reports a state; absent or empty keeps, JSON null clears; the SPA input is always empty on load"
|
||||
- "Optional meta blocks use omitempty so payloads without the feature stay byte-identical to the PHP-shaped fixtures"
|
||||
- "Settings-only compile rules (settingsFormOnly) keep repeater and dependsOn refused on settings and relation forms while admitting richeditor and tab"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- modules/cabana/field_money.go
|
||||
- modules/cabana/settings_secret.go
|
||||
- modules/cabana/settings_secret_test.go
|
||||
- modules/cabana/payments_smoke_test.go
|
||||
- modules/cabana/testdata/payments/controllers/invoices/config_form.yaml
|
||||
- modules/cabana/testdata/payments/controllers/invoices/config_list.yaml
|
||||
- modules/cabana/testdata/payments/models/invoice/fields.yaml
|
||||
- modules/cabana/testdata/payments/models/invoice/columns.yaml
|
||||
- modules/cabana/testdata/payments/models/vault/fields.yaml
|
||||
- admin/src/app/money.ts
|
||||
- admin/src/components/form/fields/MoneyField.vue
|
||||
- admin/tests/form/MoneyField.test.ts
|
||||
- admin/tests/views/SettingsFormView.test.ts
|
||||
modified:
|
||||
- modules/pact/capabilities.go
|
||||
- modules/pact/README.md
|
||||
- modules/cabana/form_schema.go
|
||||
- modules/cabana/list_schema.go
|
||||
- modules/cabana/schema_types.go
|
||||
- modules/cabana/crud.go
|
||||
- modules/cabana/http.go
|
||||
- modules/cabana/registry.go
|
||||
- modules/cabana/relation_form.go
|
||||
- modules/cabana/relation_child.go
|
||||
- modules/cabana/settings.go
|
||||
- modules/cabana/field_dependent.go
|
||||
- modules/cabana/admin_openapi.go
|
||||
- modules/cabana/README.md
|
||||
- modules/cabana/repeater_test.go
|
||||
- modules/cabana/richeditor_test.go
|
||||
- modules/cabana/companions_postgres_test.go
|
||||
- modules/cabana/openapi_conformance_test.go
|
||||
- modules/phrasebook/backend/lang/en/lang.yaml
|
||||
- modules/phrasebook/backend/lang/pl/lang.yaml
|
||||
- admin/openapi/admin.json
|
||||
- admin/src/api/schema.d.ts
|
||||
- admin/src/components/form/registry.ts
|
||||
- admin/src/components/form/formContext.ts
|
||||
- admin/src/components/form/PreviewField.vue
|
||||
- admin/src/components/form/RecordActions.vue
|
||||
- admin/src/components/form/fields/PasswordField.vue
|
||||
- admin/src/components/list/CellValue.vue
|
||||
- admin/src/components/list/DataTable.vue
|
||||
- admin/src/views/PreviewView.vue
|
||||
- admin/src/views/SettingsFormView.vue
|
||||
- admin/src/styles/main.css
|
||||
- admin/tests/form/PasswordField.test.ts
|
||||
- admin/tests/form/RecordActions.test.ts
|
||||
- admin/tests/form/PreviewView.test.ts
|
||||
- admin/tests/list/CellValue.test.ts
|
||||
- modules/boardwalk/dist
|
||||
- docs/backend/forms.md
|
||||
- docs/backend/lists-and-filters.md
|
||||
- docs/backend/settings.md
|
||||
- docs/backend/admin-controllers.md
|
||||
|
||||
key-decisions:
|
||||
- "Release gate: the framework tag v0.1.25 is held until the phase gate after plan 07 (human reply verbatim: hold); nothing pushed, nothing tagged; plans 02 to 06 build against this checkout through go.work replace"
|
||||
- "MoneyField emits the raw compact text for a malformed or too-precise draft so the server 422 stays the gate; it never emits a rounded or truncated value"
|
||||
- "SettingsResult.Meta and RecordAction.Danger use omitempty so payloads without secrets, notices or danger actions stay byte-identical"
|
||||
- "The money list column gate also checks the model column type at list compile and shares the boot message `<field>: money needs a string column`"
|
||||
- "settings_secret_test.go is an internal-package test because the compile-only password gate needs the unexported compileSetting"
|
||||
|
||||
patterns-established:
|
||||
- "Decimal money contract: string on the wire, Intl formatting from the string, scale 2..4 and max enforced server-side with named 422 keys"
|
||||
- "Write-only settings secrets with meta.secrets states and keep/clear semantics"
|
||||
- "Settings forms share the richeditor sanitize path (seaglass, meta.notices) with controller forms"
|
||||
- "Danger record actions: the flag travels pact -> cabana meta.actions -> RecordActions dialog confirm variant"
|
||||
|
||||
requirements-completed: [SC-3, D-07, D-08, D-09, D-10, D-13]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "Money form field and list column: decimal-string contract, currency/currencyFrom/scale/max keys, boot gates, 422 keys, untouched value re-saved byte-identical, locale formatting from the string, right-aligned tabular cells"
|
||||
requirement: "D-07, D-09, D-10"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "modules/cabana/payments_smoke_test.go#TestMoneyFieldCompile"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "modules/cabana/payments_smoke_test.go#TestMoneyListCompile"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "modules/cabana/payments_smoke_test.go#TestMoneyFieldSmoke (Docker, run and passed)"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "modules/cabana/payments_smoke_test.go#TestMoneyListSmoke (Docker, run and passed)"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/form/MoneyField.test.ts#submits an untouched 85.4193 byte-identical: no emit on load or blur"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/form/MoneyField.test.ts#refuses 12.34567 at scale 4 on blur, neither rounded nor truncated"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/form/MoneyField.test.ts#emits the canonical dot string for \"1 234,56\" and null for empty"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/form/MoneyField.test.ts#formats through Intl with the string itself, keeping four decimals"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/list/CellValue.test.ts#money column (UI-SPEC F1, D-07)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "Settings secrets (F2): password on lagoon.Encrypted, write-only GET with meta.secrets stored/env/empty, keep on absent or empty, clear on null, value never in a toast, error or log"
|
||||
requirement: "D-13"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "modules/cabana/settings_secret_test.go#TestSettingsSecretCompile"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "modules/cabana/payments_smoke_test.go#TestSettingsSecretSmoke (Docker, run and passed)"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "modules/cabana/openapi_conformance_test.go#TestPhase10OpenAPIConformance (GET /settings/{code} asserts meta.secrets)"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/form/PasswordField.test.ts#settings secret states (UI-SPEC F2, D-13)"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/views/SettingsFormView.test.ts#settings secrets (UI-SPEC F2, D-13)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "Settings richeditor (F3) and tabs (F4): seaglass sanitize on settings PUT with meta.notices, repeater and dependsOn still refused, FormTabs on SettingsFormView with the first error tab activated after a 422"
|
||||
requirement: "D-13, D-08"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "modules/cabana/payments_smoke_test.go#TestSettingsRichEditorSmoke (Docker, run and passed)"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "modules/cabana/richeditor_test.go#TestRichEditor (settings accept richeditor, refuse repeater)"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/views/SettingsFormView.test.ts#settings tabs (UI-SPEC F4)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "Danger record actions (F5), preview Edit rule (F6) and the table kit (F7)"
|
||||
requirement: "D-10"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "modules/cabana/payments_smoke_test.go#TestRecordActionDangerSmoke (Docker, run and passed)"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/form/RecordActions.test.ts#danger record actions (UI-SPEC F5)"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "admin/tests/form/PreviewView.test.ts#preview edit rule (UI-SPEC F6)"
|
||||
status: pass
|
||||
human_judgment: true
|
||||
rationale: "The kit classes (.summer-table*, .summer-stat__note) are CSS only; their rendering against the UI-SPEC values needs a browser check once plans 03 and 04 use them"
|
||||
- id: D5
|
||||
description: "Docs, OpenAPI, TypeScript types and the embedded dist ship with the features"
|
||||
requirement: "SC-3"
|
||||
verification:
|
||||
- kind: other
|
||||
ref: "go run ./cmd/summer docs:build --check (clean); go test -run '^(TestDocsTree|TestDelegateCommandsHelp)$' ./cmd/summer"
|
||||
status: pass
|
||||
- kind: other
|
||||
ref: "scripts/check-admin-openapi.sh --check (clean); scripts/check-admin-dist.sh (matches)"
|
||||
status: pass
|
||||
- kind: other
|
||||
ref: "npm --prefix admin run typecheck (clean); npm --prefix admin test (1360 passed, 90 files)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D6
|
||||
description: "Release decision: tag v0.1.25 proposed, human chose hold; nothing pushed or tagged"
|
||||
requirement: "SC-3"
|
||||
verification:
|
||||
- kind: other
|
||||
ref: "git ls-remote --tags origin refs/tags/v0.1.25 prints nothing; git log --oneline origin/master..master lists 20385e5, 0c86051, 1e841b4"
|
||||
status: pass
|
||||
human_judgment: true
|
||||
rationale: "Publishing a one-way framework surface is a human decision (gate blocking-human); the human replied hold"
|
||||
|
||||
duration: 43min
|
||||
completed: 2026-10-13
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 18 Plan 01: Framework admin pieces Summary
|
||||
|
||||
**Cabana money field and column on a decimal-string contract, write-only settings secrets with meta.secrets states, richeditor and tabs on settings forms, danger record actions, the preview Edit rule and the table kit, shipped in OpenAPI, types, docs and the embedded dist; framework tag v0.1.25 held until the phase gate on the human's `hold`**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** about 43 min of execution (started 2026-10-13T09:04:22Z, last task commit 2026-10-13T09:36:12Z, close-out after the human checkpoint at about 09:47Z)
|
||||
- **Task 1 commit:** 2026-10-13T09:15:05Z
|
||||
- **Task 2 commit:** 2026-10-13T09:22:36Z
|
||||
- **Task 3 commit:** 2026-10-13T09:36:12Z
|
||||
- **Tasks:** 4 of 4 (3 auto, 1 checkpoint:decision answered `hold`)
|
||||
- **Files modified:** 59 (53 source, test and docs files plus 6 modules/boardwalk/dist assets)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **Money field (D-07, amended D-09, D-10).** `type: money` in cabana with `currency: <ISO>` or `currencyFrom: <sibling column>` (exactly one), `scale` 2 to 4 (default 4) and an optional `max` decimal string. The value is a decimal string with a dot, never a float and never rounded: too many fraction digits is a 422 `money.too_precise`, a non-decimal `money.invalid`, above max `money.too_large`. Two sources, none, an unknown sibling, a model column that is not a string (or sql.Scanner plus driver.Valuer) or a scale outside 2..4 fail boot naming the field. A stored `85.4193` is re-saved byte-identical when untouched.
|
||||
- **Money list column.** `type: money` with the same currency keys renders a locale-formatted amount right-aligned in tabular numerals; each row carries the `currencyFrom` column's value under that key so the SPA never guesses a currency; the amount shows without a code when the row has none and an em dash when empty.
|
||||
- **MoneyField.vue and money.ts.** Decimal-mode text input with the currency suffix, blur validation (`money.invalid`, `money.too_precise`) with the danger border, `1 234,56` parsed to `1234.56`, empty input emits null, read-only and preview show the formatted amount or a muted em dash, `Intl.NumberFormat` receives the string itself so `99999999.9999` keeps all four decimals in en and pl.
|
||||
- **Settings secrets (F2, D-13).** A settings form accepts `type: password` on a `lagoon.Encrypted` column. The GET never includes the value and reports `meta.secrets.<field>` as `stored`, `env` (empty column and the model's `SecretFallbacks()` config key resolves non-empty) or `empty`; on PUT an absent key or empty string keeps the stored value and JSON null clears it; the PUT response carries the new states. PasswordField is always empty on load with the status line, Remove stored value sends null with an Undo, and the typed value never appears after the save response.
|
||||
- **Settings richeditor (F3) and tabs (F4).** Settings PUT sanitizes richeditor values through seaglass like a controller form and returns removals as `meta.notices`; settings and relation forms still refuse `repeater` and `dependsOn` at boot. SettingsFormView renders FormTabs with untabbed fields on the default tab first, the card inside the panel, and activates the first tab with an error (with its count chip) after a 422.
|
||||
- **Danger record actions (F5).** `pact.AdminRecordAction.Danger` (wire `danger`, omitempty) flows through cabana `meta.actions` to RecordActions.vue, whose confirm dialog button takes the danger variant while the footer button stays outline; 409, 404, 403 and failure handling unchanged, both dialog buttons disabled during the POST.
|
||||
- **Preview Edit rule (F6).** PreviewView renders Edit only when the form has at least one registered value-holding field whose context allows update (`hasEditableField`); a preview of partial-only fields shows no Edit and no primary fill.
|
||||
- **Table kit (F7).** `.summer-table-wrap`, `.summer-table`, `th`, `td`, `__num`, `__sub`, `tfoot td` and `.summer-stat__note` in main.css reading only `--c-*` variables; listed in the cabana README kit section.
|
||||
- **Shipping.** Phrase keys `money.*` and `secret.*` (en, pl), swag annotations regenerated into `admin/openapi/admin.json` and `admin/src/api/schema.d.ts`, rebuilt `modules/boardwalk/dist`, docs in forms (Money), lists-and-filters (Money columns), settings (secrets, richeditor, tabs) and admin-controllers (Danger).
|
||||
|
||||
## Exported identifiers added per module
|
||||
|
||||
| Module | Identifiers |
|
||||
|--------|-------------|
|
||||
| pact | `AdminRecordAction.Danger bool` (json `danger,omitempty`) |
|
||||
| cabana | `SecretFallbacks` interface; `SettingsMeta` struct (`Secrets map[string]string`, `Notices []FieldNotice`); `SettingsResult.Meta *SettingsMeta` (json `meta,omitempty`); `SettingsService` gained the fields `Tr *phrasebook.Translator` and `Config *compass.Config` (positional literals of the struct no longer compile; keyed literals and the zero value keep working); `FormField.Currency`, `.CurrencyFrom`, `.Scale`, `.Max`; `ListColumn.Currency`, `.CurrencyFrom`; `RecordAction.Danger`; form field type `money`; list column type `money`; settings field types `password` (on lagoon.Encrypted) and `richeditor`; settings field key `tab` |
|
||||
| phrasebook | `backend::lang.money.{invalid,too_precise,too_large,currency}`, `backend::lang.secret.{stored,env,empty,clear,clear_pending,undo,placeholder_stored}` |
|
||||
| admin (SPA) | `money.ts`: `parseMoneyInput`, `trimMoneyDisplay`, `decimalSeparator`, `formatMoney`, `MoneyParseError`, `MoneyParseResult`; `MoneyField.vue`; `formContext.ts` `SETTINGS_SECRETS` injection key; CSS kit classes above |
|
||||
|
||||
## Task Commits
|
||||
|
||||
1. **Task 1: Tracer: money field end to end (schema, SPA input, save, OpenAPI, docs, dist)** - `20385e5` (feat)
|
||||
2. **Task 2: Money list column, danger record actions (F5), preview Edit rule (F6), table kit (F7)** - `0c86051` (feat)
|
||||
3. **Task 3: Settings: write-only secrets (F2), richeditor (F3), tabs (F4)** - `1e841b4` (feat)
|
||||
4. **Task 4: Release gate** - checkpoint:decision (blocking-human), no commit. Human reply verbatim: `hold`
|
||||
|
||||
**Measured commit count:** `git rev-list --count 99ccae2..1e841b4` = 3, all this plan's. The plan metadata commit (this SUMMARY, STATE.md, ROADMAP.md) follows this file; see `git log --oneline --grep="18-01"`.
|
||||
|
||||
## Release Sheet (Task 4)
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Gate | release-gate-ok: go vet clean; `go test -count=1 -short ./...` green; cabana inventories 4/4 (TestPhase09ContractInventory, TestPhase09PermissionMatrix, TestPhase10ConformanceInventory, TestTokenRouteGates); TestDocsTree and TestDelegateCommandsHelp ok; `summer docs:build --check` clean; admin typecheck clean; vitest 1360 passed in 90 files; check-admin-dist.sh matches; check-admin-openapi.sh --check clean |
|
||||
| Docker smoke tests | ALL RUN and PASSED on this host: TestMoneyFieldSmoke, TestMoneyListSmoke, TestRecordActionDangerSmoke, TestSettingsSecretSmoke, TestSettingsRichEditorSmoke, TestPhase10OpenAPIConformance, TestRichEditor (none recorded NOT RUN) |
|
||||
| 18-REVIEW.md / 18-SECURITY.md | absent, no open high findings |
|
||||
| Concurrency | `git status --porcelain -- modules admin docs scripts cmd go.mod go.sum` empty; `git log --oneline HEAD..origin/master` empty |
|
||||
| Proposed tag | **v0.1.25** (highest existing v0.1.24; absent on origin) |
|
||||
| One-way surfaces it would publish | cabana field type `money` with keys currency, currencyFrom, scale, max; list column `money`; settings `password` on lagoon.Encrypted with `meta.secrets` and the keep/clear semantics; `cabana.SecretFallbacks`; `richeditor` and `tab` on settings forms; `pact.AdminRecordAction.Danger`; phrase keys `money.*` and `secret.*`; the kit classes |
|
||||
| Commits that would be pushed | `20385e5`, `0c86051`, `1e841b4` |
|
||||
| go.work note | plans 02 to 06 build against this checkout through go.work replace and need no tag; holding costs only a later bump |
|
||||
| Human reply (verbatim) | `hold` |
|
||||
|
||||
## Release decision
|
||||
|
||||
The human chose **hold**: the framework tag is held until the phase gate after plan 07 (unit tests), so one tag carries the whole phase with its tests. **Nothing was pushed and nothing was tagged** in this plan. Acceptance for `hold` confirmed at close-out: `git ls-remote --tags origin refs/tags/v0.1.25` prints nothing; `git log --oneline origin/master..master` lists `1e841b4`, `0c86051`, `20385e5` above `99ccae2`. Plans 02 to 06 build against this checkout through `go.work` replace lines until the phase-gate tag.
|
||||
|
||||
Close-out sanity check (after the checkpoint, nothing changed): `go vet ./... && go test -count=1 -short ./...` exit 0, 40 packages ok.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
See the `key-files` frontmatter: 13 created (field_money.go, settings_secret.go and its test, payments_smoke_test.go, five payments testdata YAML files, money.ts, MoneyField.vue, MoneyField.test.ts, SettingsFormView.test.ts) and 40 modified (pact capabilities and README; cabana schema compile, crud, http, settings, dependent gate, OpenAPI annotations, README and four maintained tests; phrasebook en and pl; admin form, list, preview and settings components, formContext, main.css, four maintained vitest files, OpenAPI JSON and schema.d.ts; four docs pages) plus 6 `modules/boardwalk/dist` assets.
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- **Release: hold.** Tag v0.1.25 proposed, human replied `hold`; no push, no tag; phase-gate tag after plan 07.
|
||||
- **MoneyField emits the raw compact text for a malformed or too-precise draft**, so the server 422 stays the gate; it never emits a rounded or truncated value (D-10 forbids truncation).
|
||||
- **`SettingsResult.Meta` and `RecordAction.Danger` use omitempty**, so payloads without secrets, notices or danger actions stay byte-identical and the Phase 10 conformance fixtures are untouched.
|
||||
- **The money list column gate also checks the model column type at list compile** and shares the message `<field>: money needs a string column` with the form compile.
|
||||
- **No new conformance case:** the existing `GET /settings/{code}` case asserts `meta.secrets` through the conform settings fixture's new password field.
|
||||
- **Threat model T-18-01..05 mitigations** (money never a float, no rounding, write-only secrets, no secret in toast or log, danger confirm) are implemented and covered by the smoke tests and vitest. No npm or Go dependency added.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] MoneyField emits the raw compact text, not "nothing new", for a malformed or too-precise draft**
|
||||
- **Found during:** Task 1 (MoneyField blur validation)
|
||||
- **Issue:** The literal plan wording ("emit nothing new") would have let Save write the last valid prefix of the draft, a truncation D-10 forbids.
|
||||
- **Fix:** The field emits the raw compact text so the server answers 422 (`money.invalid` or `money.too_precise`) and remains the gate; the test asserts no rounded or truncated value is emitted.
|
||||
- **Files modified:** admin/src/components/form/fields/MoneyField.vue, admin/tests/form/MoneyField.test.ts
|
||||
- **Verification:** `refuses 12.34567 at scale 4 on blur, neither rounded nor truncated` (vitest), TestMoneyFieldSmoke (422 too_precise)
|
||||
- **Committed in:** `20385e5`
|
||||
|
||||
**2. [Rule 3 - Blocking] Existing tests maintained for the new type and the relaxed settings gate**
|
||||
- **Found during:** Tasks 1 and 3
|
||||
- **Issue:** repeater_test.go's typeKeys inventory did not know `money`; richeditor_test.go and companions_postgres_test.go asserted that a settings form refuses richeditor.
|
||||
- **Fix:** typeKeys gained `money`; the two tests now expect a settings form to accept richeditor and still refuse repeater.
|
||||
- **Files modified:** modules/cabana/repeater_test.go, modules/cabana/richeditor_test.go, modules/cabana/companions_postgres_test.go
|
||||
- **Verification:** `go test -short ./modules/cabana`, TestRichEditor (Docker, passed)
|
||||
- **Committed in:** `20385e5`, `1e841b4`
|
||||
|
||||
**3. [Rule 2 - Missing test access] settings_secret_test.go added as an internal-package test**
|
||||
- **Found during:** Task 3
|
||||
- **Issue:** The compile-only password gate (password on a non-Encrypted column, SecretFallbacks resolution) needs the unexported `compileSetting`; an external test package could not reach it.
|
||||
- **Fix:** Added modules/cabana/settings_secret_test.go in package cabana; docs/backend/settings.md's snippet references its `secretSettings.SecretFallbacks`.
|
||||
- **Files modified:** modules/cabana/settings_secret_test.go (new), docs/backend/settings.md
|
||||
- **Verification:** TestSettingsSecretCompile; `summer docs:build --check` clean
|
||||
- **Committed in:** `1e841b4`
|
||||
|
||||
**4. [Rule 2 - Wire compatibility] omitempty on `SettingsResult.Meta` and `RecordAction.Danger`**
|
||||
- **Found during:** Tasks 2 and 3
|
||||
- **Issue:** Always serializing `meta` and `danger` would change every existing settings and record-action payload and the Phase 10 conformance fixtures.
|
||||
- **Fix:** `Meta *SettingsMeta json:"meta,omitempty"` (`data.meta.secrets` on the wire) is nil when there is no password field and no notice; `Danger` is `omitempty`.
|
||||
- **Files modified:** modules/cabana/settings.go, modules/cabana/settings_secret.go, modules/cabana/schema_types.go
|
||||
- **Verification:** TestPhase10OpenAPIConformance (Docker, passed), check-admin-openapi.sh --check
|
||||
- **Committed in:** `0c86051`, `1e841b4`
|
||||
|
||||
**5. [Rule 2 - Missing gate] Money list column checks the model column type at list compile**
|
||||
- **Found during:** Task 2
|
||||
- **Issue:** The plan named the model-type check for the form field only; a list column over a float column would have formatted a float.
|
||||
- **Fix:** `checkMoneyColumn` runs at list compile too; the Task 1 boot-failure subtest matches the shared message `total: money needs a string column`.
|
||||
- **Files modified:** modules/cabana/field_money.go, modules/cabana/list_schema.go, modules/cabana/payments_smoke_test.go
|
||||
- **Verification:** TestMoneyListCompile
|
||||
- **Committed in:** `0c86051`
|
||||
|
||||
### Not deviations
|
||||
|
||||
- No new OpenAPI conformance case was added: the existing `GET /settings/{code}` case covers `meta.secrets` through the conform fixture's new password field.
|
||||
- The tracer gate after Task 1 auto-continued (interactive run, `human_verify_mode: end-of-phase`, automated-only verify re-run green), per the executor's tracer rules.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None beyond the deviations above. All Docker smoke suites ran on this host (Docker available), so no suite is recorded NOT RUN.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- Plan 02 (paymentgateway core) can start: the framework pieces it needs (money field and column, settings secrets with `SecretFallbacks`, richeditor and tabs on settings, `AdminRecordAction.Danger`, the kit classes) are on master at `1e841b4`.
|
||||
- Plans 02 to 06 build against this checkout through `go.work` replace; the framework tag (proposed v0.1.25) is **held** until the phase gate after plan 07.
|
||||
- Plans 03 and 04 should give the table kit and the money previews a browser check (coverage D4 human judgment).
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- Key files exist on disk: modules/cabana/field_money.go, modules/cabana/settings_secret.go, modules/cabana/payments_smoke_test.go, admin/src/app/money.ts, admin/src/components/form/fields/MoneyField.vue, admin/tests/form/MoneyField.test.ts, admin/tests/views/SettingsFormView.test.ts.
|
||||
- Commits exist: `20385e5`, `0c86051`, `1e841b4` (`git log --oneline --all --grep="18-01"` lists them).
|
||||
- `go vet ./... && go test -count=1 -short ./...` exit 0 at close-out.
|
||||
- `git ls-remote --tags origin refs/tags/v0.1.25` empty; `git log --oneline origin/master..master` lists the three task commits.
|
||||
Reference in New Issue
Block a user