diff --git a/.planning/phases/10.1-runtime-admin-extension-point/10.1-VALIDATION.md b/.planning/phases/10.1-runtime-admin-extension-point/10.1-VALIDATION.md new file mode 100644 index 0000000..6774ac1 --- /dev/null +++ b/.planning/phases/10.1-runtime-admin-extension-point/10.1-VALIDATION.md @@ -0,0 +1,96 @@ +--- +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: draft +nyquist_compliant: false +wave_0_complete: false +created: "2026-09-28" +--- + +# Phase 10.1 — Validation Strategy + +> Per-phase validation contract for feedback sampling during execution. Seeded from `10.1-RESEARCH.md` § Validation Architecture; task IDs are filled in once PLAN.md files exist. + +--- + +## 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` (new) plus `scripts/check-phase10.sh --all` staying green | +| **Estimated runtime** | ~120 seconds with warm caches (Postgres containers dominate) | + +--- + +## 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 ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status | +|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------| +| TBD | TBD | TBD | D-06, D-09 | T-10.1-11 | widget/partial types; per-type keys; tag prefix `{vendor}-{plugin}-`; unknown key fails boot | unit | `go test ./modules/cabana -run '^TestPhase101FormExtensionSchema$' -count=1` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | 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` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | D-11 | — | `headerPartial` compiles; missing template / parse error / missing view model fails boot | unit | `go test ./modules/cabana -run '^TestPhase101PartialSchema$' -count=1` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | 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 caps | unit | `go test ./modules/cabana -run '^TestPhase101PartialSanitizer$' -count=1` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | D-12 | T-10.1-05 | toolbar names resolved against registered actions; unknown fails boot; permission-filtered | unit | `go test ./modules/cabana -run '^TestPhase101Toolbar$' -count=1` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | 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` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | D-05 | T-10.1-04 | new POSTs refused without X-Requested-With | unit | `go test ./modules/cabana -run '^TestPhase10CSRF$' -count=1` | ✅ | ⬜ pending | +| TBD | TBD | TBD | 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` | ✅ extend | ⬜ pending | +| TBD | TBD | TBD | D-14 | T-10.1-11, T-10.1-13 | loader idempotent; foreign URL refused; CSS disabled off-controller | vitest | `npm --prefix admin test -- tests/app/pluginAssets.test.ts` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | D-05, D-07, D-08 | T-10.1-07 | widget attributes; event → POST; patch only fill keys; create mode | vitest | `npm --prefix admin test -- tests/form/WidgetField.test.ts` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | D-17 | T-10.1-08 | PartialHost renders via h(); unknown tag/attr dropped; text stays text | vitest | `npm --prefix admin test -- tests/list/PartialHost.test.ts tests/form/PartialField.test.ts` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | D-03, D-12 | — | list header slot; custom toolbar button → POST → toast → reload | vitest | `npm --prefix admin test -- tests/list/ListToolbar.test.ts tests/views/ListView.test.ts` | ✅ extend | ⬜ pending | +| TBD | TBD | TBD | D-09 | — | widget/partial registered, excluded from save body, render on create | vitest | `npm --prefix admin test -- tests/form/registry.test.ts tests/form/formState.test.ts` | ✅ extend | ⬜ pending | +| TBD | TBD | TBD | D-17 | T-10.1-08, T-10.1-10 | no raw-HTML sinks; plugin assets contain no fetch/XMLHttpRequest/document.cookie | gate | `scripts/check-phase10.1.sh --self-test && scripts/check-phase10.1.sh --hygiene` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | D-04 | — | committed dist matches source | gate | `scripts/check-admin-dist.sh` | ✅ | ⬜ pending | +| TBD | TBD | TBD | D-01, D-02, D-03, D-12 | T-10.1-05, T-10.1-06, T-10.1-09 | Albums stats strip scoped per collection; widget stub fills; toolbar action toasts; limited admin 403; assets served | integration (Postgres) | `cd ../fonoteka.go && go test ./plugins/golem15/fonoteka -run '^TestPhase101AlbumsExtension$' -count=1` | ❌ W0 | ⬜ pending | +| TBD | TBD | TBD | D-01 | — | framework repo has no Płytarium names | gate | `scripts/check-phase10.sh --hygiene` | ✅ | ⬜ pending | + +*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* + +--- + +## Wave 0 Requirements + +- [ ] `modules/cabana/testdata/extension/` — acme fixture plugin tree (config YAML, `_stats.htm`, `_summary.htm`, fields/columns, `assets/js/lookup.js`, `assets/css/gadgets.css`) +- [ ] `modules/cabana/phase101_*_test.go` — schema, sanitizer, assets, actions, toolbar +- [ ] `admin/tests/fixtures/extension.*.json` — list/form schema with assets, widget, partial, toolbar actions; partial nodes +- [ ] `admin/tests/app/pluginAssets.test.ts`, `tests/form/WidgetField.test.ts`, `tests/form/PartialField.test.ts`, `tests/list/PartialHost.test.ts` +- [ ] `../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_albums_test.go` +- [ ] `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 | D-13, D-16, D-07 | happy-dom does not enforce CSP or real module loading | `summer serve` for fonoteka, open `/plytadmin` Albums form in a browser, check console for CSP errors, click the Discogs widget, save, reload | + +--- + +## Validation Sign-Off + +- [ ] All tasks have `` verify or Wave 0 dependencies +- [ ] Sampling continuity: no 3 consecutive tasks without automated verify +- [ ] Wave 0 covers all MISSING references +- [ ] No watch-mode flags +- [ ] Feedback latency < 120s +- [ ] `nyquist_compliant: true` set in frontmatter + +**Approval:** pending