--- phase: "10.1" slug: "runtime-admin-extension-point" # status lifecycle: draft (seeded by plan-phase) → validated (set by validate-phase §6) # audit-milestone §5.5 distinguishes NOT-VALIDATED (draft) from PARTIAL (validated + nyquist_compliant: false) (#2117) status: validated nyquist_compliant: true wave_0_complete: true created: "2026-09-28" validated: "2026-09-29" --- # Phase 10.1 — Validation Strategy > Per-phase validation contract for feedback sampling during execution. Seeded from `10.1-RESEARCH.md` § Validation Architecture; task IDs and statuses are filled in from the executed plans 10.1-01 to 10.1-04 and the final run of `scripts/check-phase10.1.sh --all`. Requirement: ADMIN-07. --- ## Test Infrastructure | Property | Value | |----------|-------| | **Framework** | Go `testing` (+ testcontainers Postgres); Vitest 3.2.7 + happy-dom 20.11.6 + @vue/test-utils 2.4.11 | | **Config file** | `admin/vitest.config.ts`; `go.mod` / `go.work` | | **Quick run command** | `go test ./modules/cabana -run 'TestPhase101' -count=1` and `npm --prefix admin test -- tests/form tests/list tests/app` | | **Full suite command** | `go vet ./... && go test ./...` in both repos, `npm --prefix admin run typecheck && npm --prefix admin test` | | **Phase gate** | `scripts/check-phase10.1.sh --all` plus `scripts/check-phase10.sh --all` staying green | | **Estimated runtime** | ~120 seconds with warm caches for the test commands; the full gate (npm ci, both repositories, every Postgres suite) takes several minutes | --- ## Sampling Rate - **After every task commit:** the quick run command for the touched side (cabana `-run TestPhase101` or the touched vitest files), plus `go vet ./...` - **After every plan wave:** full suite in both repos, `scripts/check-admin-openapi.sh --check`, and `scripts/check-admin-dist.sh` after SPA changes - **Before `/gsd-verify-work`:** `scripts/check-phase10.1.sh --all` and `scripts/check-phase10.sh --all` green - **Max feedback latency:** 120 seconds --- ## Per-Task Verification Map Task IDs are `{plan}-T{n}`. A row lists the task that built the behaviour and the 10.1-04 task that brought its full tests. Every row's command passed in the final gate run. | Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status | |---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------| | 10.1-01-T1, 10.1-04-T1 | 10.1-01, 10.1-04 | 1, 4 | ADMIN-07 (D-06, D-09) | T-10.1-11 | widget/partial types; per-type keys; tag prefix `{vendor}-{plugin}-`; reserved names; unknown key and settings-form use fail boot | unit | `go test ./modules/cabana -run '^TestPhase101FormExtensionSchema$' -count=1` | ✅ | ✅ green | | 10.1-01-T1, 10.1-04-T1 | 10.1-01, 10.1-04 | 1, 4 | ADMIN-07 (D-07) | T-10.1-05, T-10.1-06, T-10.1-07 | fill ⊆ writable fields; server drops extra keys; action permission + scoped record load | unit + Postgres | `go test ./modules/cabana -run '^TestPhase101Actions$' -count=1` | ✅ | ✅ green | | 10.1-01-T3, 10.1-04-T1 | 10.1-01, 10.1-04 | 1, 4 | ADMIN-07 (D-11) | — | `headerPartial` compiles; missing template / parse error / missing view model / Winter path fails boot | unit | `go test ./modules/cabana -run '^TestPhase101PartialSchema$' -count=1` | ✅ | ✅ green | | 10.1-01-T3, 10.1-04-T1 | 10.1-01, 10.1-04 | 1, 4 | ADMIN-07 (D-10, D-17) | T-10.1-08, T-10.1-09, T-10.1-12 | allowlisted node tree; script/on*/style/javascript: dropped; record data escaped; size, node and depth caps; model view model refused | unit | `go test ./modules/cabana -run '^TestPhase101PartialSanitizer$' -count=1` | ✅ | ✅ green | | 10.1-01-T2, 10.1-04-T1 | 10.1-01, 10.1-04 | 1, 4 | ADMIN-07 (D-12) | T-10.1-05 | toolbar names resolved against registered actions; unknown fails boot; permission-filtered and localized per request | unit | `go test ./modules/cabana -run '^TestPhase101Toolbar$' -count=1` | ✅ | ✅ green | | 10.1-01-T2, 10.1-04-T1 | 10.1-01, 10.1-04 | 1, 4 | ADMIN-07 (D-13, D-16) | T-10.1-01, T-10.1-02, T-10.1-03 | exact asset allowlist; MIME + nosniff + CORP; ETag/304; traversal/undeclared → SPA fall-through | unit | `go test ./modules/cabana -run '^TestPhase101Assets$' -count=1` | ✅ | ✅ green | | 10.1-01-T2, 10.1-04-T1 | 10.1-01, 10.1-04 | 1, 4 | ADMIN-07 (D-16) | T-10.1-02 | exported `ContentType` and `SetSecurityHeaders` | unit | `go test ./modules/boardwalk -run '^TestPhase101BoardwalkExports$' -count=1` | ✅ | ✅ green | | 10.1-01-T1, 10.1-01-T2 | 10.1-01 | 1 | ADMIN-07 (D-05) | T-10.1-04 | new POSTs refused without X-Requested-With | unit | `go test ./modules/cabana -run '^TestPhase10CSRF$' -count=1` | ✅ | ✅ green | | 10.1-01-T1, 10.1-01-T2, 10.1-01-T3 | 10.1-01 | 1 | ADMIN-07 (D-05, D-12) | T-10.1-04 | route inventory, permission matrix and OpenAPI conformance cover the new routes | unit + Postgres | `go test ./modules/cabana -run '^(TestPhase09PermissionMatrix\|TestPhase09ContractInventory\|TestPhase10OpenAPIConformance)$' -count=1 && scripts/check-admin-openapi.sh --check` | ✅ | ✅ green | | 10.1-02-T1, 10.1-02-T3, 10.1-04-T2 | 10.1-02, 10.1-04 | 2, 4 | ADMIN-07 (D-14) | T-10.1-13, T-10.1-16 | loader idempotent; foreign and dot-segment URLs refused (encoded too); CSS disabled off-controller | vitest | `npm --prefix admin test -- tests/app/pluginAssets.test.ts` | ✅ | ✅ green | | 10.1-02-T1, 10.1-04-T2 | 10.1-02, 10.1-04 | 2, 4 | ADMIN-07 (D-05, D-07, D-08) | T-10.1-17, T-10.1-18 | widget attributes only; event → POST once while busy; patch only fill keys; 5000 ms timeout; create mode | vitest | `npm --prefix admin test -- tests/form/WidgetField.test.ts` | ✅ | ✅ green | | 10.1-02-T2, 10.1-04-T2 | 10.1-02, 10.1-04 | 2, 4 | ADMIN-07 (D-09, D-17) | T-10.1-14 | PartialHost renders via h(); unknown tag/attr dropped; text stays text; skeleton, empty, failure and busy-refetch states | vitest | `npm --prefix admin test -- tests/list/PartialHost.test.ts tests/form/PartialField.test.ts` | ✅ | ✅ green | | 10.1-02-T3, 10.1-04-T2 | 10.1-02, 10.1-04 | 2, 4 | ADMIN-07 (D-03, D-12) | — | list header slot; custom toolbar button → POST {} → toast → reload; header refetch only after bulk delete and actions | vitest | `npm --prefix admin test -- tests/list/ListToolbar.test.ts tests/list/ListView.test.ts` | ✅ | ✅ green | | 10.1-02-T1, 10.1-02-T2, 10.1-04-T2 | 10.1-02, 10.1-04 | 2, 4 | ADMIN-07 (D-09) | — | widget/partial registered, excluded from save body, span labels, render on create, form context provided | vitest | `npm --prefix admin test -- tests/form/registry.test.ts tests/form/formState.test.ts tests/form/FormField.test.ts tests/form/FormView.test.ts` | ✅ | ✅ green | | 10.1-02-T1, 10.1-02-T2, 10.1-02-T3 | 10.1-02 | 2 | ADMIN-07 (D-04, D-05, D-17) | T-10.1-14, T-10.1-18 | end-to-end SPA smoke of widget, partials, toolbar actions and scoped stylesheets; framework strings resolve | vitest + unit | `npm --prefix admin test -- tests/smoke && go test ./modules/phrasebook -run '^TestPhase10SPAKeysResolve$' -count=1` | ✅ | ✅ green | | 10.1-04-T3 | 10.1-04 | 4 | ADMIN-07 (D-17) | T-10.1-08, T-10.1-10, T-10.1-14, T-10.1-19, T-10.1-20 | no raw-HTML sinks or HTML-string parsers; plugin assets contain no network, cookie or storage access; partial templates carry no script or handlers; detectors fail closed | gate | `scripts/check-phase10.1.sh --self-test && scripts/check-phase10.1.sh --hygiene` | ✅ | ✅ green | | 10.1-02-T1, 10.1-02-T2, 10.1-02-T3, 10.1-04-T2 | 10.1-02, 10.1-04 | 2, 4 | ADMIN-07 (D-04) | T-10.1-SC | committed dist matches source | gate | `scripts/check-admin-dist.sh` | ✅ | ✅ green | | 10.1-03-T1 | 10.1-03 | 3 | ADMIN-07 (D-01, D-03) | T-10.1-15 | Albums stats strip scoped per collection; copy in pl and en | integration (Postgres) | `cd ../fonoteka.go && go test ./plugins/golem15/fonoteka -run '^(TestPhase101AlbumsSmoke\|TestPhase10LangCatalog\|TestAlbumsAdminList\|TestAlbumsAdminRegistration\|TestPhase10ControllerCopy)$' -count=1` | ✅ | ✅ green | | 10.1-03-T2 | 10.1-03 | 3 | ADMIN-07 (D-02, D-06, D-07) | T-10.1-21, T-10.1-22 | Discogs widget stub fills year and format; a save persists them (json.Number fill) | integration (Postgres) + unit | `cd ../fonoteka.go && go test ./plugins/golem15/fonoteka -run '^(TestPhase101AlbumsSmoke\|TestAlbumsAdminForm\|TestAlbumsAdminCRUD\|TestPhase10Controllers\|TestPhase10AlbumRelations\|TestPhase10AssembledAcceptance)$' -count=1 && go test ./modules/lagoon -run '^TestFillJSONNumber$' -count=1` | ✅ | ✅ green | | 10.1-03-T3 | 10.1-03 | 3 | ADMIN-07 (D-12) | T-10.1-21 | Sync with Discogs toolbar stub toasts; limited admin 403 | integration (Postgres) | `cd ../fonoteka.go && go test ./plugins/golem15/fonoteka/... -count=1` | ✅ | ✅ green | | 10.1-03-T1, 10.1-03-T2, 10.1-03-T3, 10.1-04-T1 | 10.1-03, 10.1-04 | 3, 4 | ADMIN-07 (D-01, D-02, D-03, D-12) | T-10.1-05, T-10.1-06, T-10.1-09, T-10.1-15, T-10.1-21 | Albums stats strip over two collections; widget stub fills and saves; toolbar action toasts; Genres-only admin 403; assets served; labels resolve in pl and en; every route template in admin.json | integration (Postgres) | `cd ../fonoteka.go && go test ./plugins/golem15/fonoteka -run '^(TestPhase101AlbumsExtension\|TestPhase101AlbumsSmoke)$' -count=1` | ✅ | ✅ green | | 10.1-03-T3, 10.1-04-T1 | 10.1-03, 10.1-04 | 3, 4 | ADMIN-07 (D-01) | T-10.1-20 | framework repo has no application names | gate | `scripts/check-phase10.sh --hygiene` | ✅ | ✅ green | | 10.1-04-T3 | 10.1-04 | 4 | ADMIN-07 | T-10.1-19, T-10.1-SC | the whole phase: go, security, postgres, spa, openapi, dist, hygiene and evidence stages; Phase 10 gate still green | gate | `scripts/check-phase10.1.sh --all && scripts/check-phase10.sh --all` | ✅ | ✅ green | *Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* --- ## Wave 0 Requirements - [x] `modules/cabana/testdata/extension/` — acme fixture plugin tree (config YAML, `_stats.htm`, `_summary.htm`, fields/columns, `assets/js/lookup.js`, `assets/css/gadgets.css`, en/pl lang) - [x] `modules/cabana/phase101_*_test.go` — schema, sanitizer, assets, actions, toolbar - [x] `admin/tests/fixtures/extension.*.json` — list/form schema with assets, widget, partial, toolbar actions; partial nodes (from 10.1-02, reused) - [x] `admin/tests/app/pluginAssets.test.ts`, `tests/form/WidgetField.test.ts`, `tests/form/PartialField.test.ts`, `tests/list/PartialHost.test.ts` - [x] `../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_albums_test.go` - [x] `scripts/check-phase10.1.sh` with `--self-test`, `--go`, `--security`, `--postgres`, `--spa`, `--openapi`, `--dist`, `--hygiene`, `--evidence`, `--all` No framework install needed. --- ## Manual-Only Verifications | Behavior | Requirement | Why Manual | Test Instructions | |----------|-------------|------------|-------------------| | Browser loads plugin module script under CSP `script-src 'self'`; widget renders; fill then save persists; strip and buttons wrap at 768px with pl labels; plugin CSS off on other controllers; Vite `/assets` dev proxy | ADMIN-07 (D-13, D-16, D-07) | happy-dom does not enforce CSP, load module scripts or lay out | The human-check blocks of 10.1-02 Task 3 and 10.1-03 Task 3, collected at `/gsd-verify-work`: `summer serve` for fonoteka, open `/plytadmin` Albums in a browser, check the console for CSP errors, click the Discogs widget, save, reload | --- ## Validation Sign-Off - [x] All tasks have `` verify or Wave 0 dependencies - [x] Sampling continuity: no 3 consecutive tasks without automated verify - [x] Wave 0 covers all MISSING references - [x] No watch-mode flags - [x] Feedback latency < 120s for the per-task commands - [x] `nyquist_compliant: true` set in frontmatter **Approval:** validated 2026-09-29 by `scripts/check-phase10.1.sh --all`