285 lines
29 KiB
Markdown
285 lines
29 KiB
Markdown
---
|
|
phase: 10-admin-vue-spa
|
|
plan: 05
|
|
type: execute
|
|
wave: 5
|
|
depends_on: [10-01, 10-02, 10-03, 10-04]
|
|
files_modified:
|
|
- admin/vitest.config.ts
|
|
- admin/tests/setup.ts
|
|
- admin/tests/fixtures/**
|
|
- admin/tests/app/i18n.test.ts
|
|
- admin/tests/app/winterUrl.test.ts
|
|
- admin/tests/app/listQuery.test.ts
|
|
- admin/tests/app/runtime.test.ts
|
|
- admin/tests/app/icons.test.ts
|
|
- admin/tests/app/client.test.ts
|
|
- admin/tests/app/router.test.ts
|
|
- admin/tests/state/useAuth.test.ts
|
|
- admin/tests/state/useNavigation.test.ts
|
|
- admin/tests/state/useSidebar.test.ts
|
|
- admin/tests/state/useToasts.test.ts
|
|
- admin/tests/state/useSettings.test.ts
|
|
- admin/tests/shell/AppShell.test.ts
|
|
- admin/tests/shell/PluginRail.test.ts
|
|
- admin/tests/shell/SectionPanel.test.ts
|
|
- admin/tests/shell/SectionFlyout.test.ts
|
|
- admin/tests/shell/UserMenu.test.ts
|
|
- admin/tests/shell/Breadcrumbs.test.ts
|
|
- admin/tests/list/DataTable.test.ts
|
|
- admin/tests/list/ListToolbar.test.ts
|
|
- admin/tests/list/FilterBar.test.ts
|
|
- admin/tests/list/Pagination.test.ts
|
|
- admin/tests/list/CellValue.test.ts
|
|
- admin/tests/list/ListView.test.ts
|
|
- admin/tests/form/registry.test.ts
|
|
- admin/tests/form/FormGrid.test.ts
|
|
- admin/tests/form/FormField.test.ts
|
|
- admin/tests/form/FormTabs.test.ts
|
|
- admin/tests/form/fields.test.ts
|
|
- admin/tests/form/RelationField.test.ts
|
|
- admin/tests/form/FormView.test.ts
|
|
- admin/tests/form/Settings.test.ts
|
|
- admin/tests/relation/RelationManager.test.ts
|
|
- admin/tests/relation/RelationPickerModal.test.ts
|
|
- admin/tests/views/LoginView.test.ts
|
|
- boardwalk/boardwalk_test.go
|
|
- bouncer/cookie_guard_test.go
|
|
- cabana/phase10_coverage_test.go
|
|
- phrasebook/phase10_test.go
|
|
- surf/admin_prefix_test.go
|
|
- internal/tools/swagger2openapi/main_test.go
|
|
- ../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_e2e_test.go
|
|
- scripts/check-phase10.sh
|
|
- .planning/phases/10-admin-vue-spa/10-SECURITY-REVIEW.md
|
|
- .planning/phases/10-admin-vue-spa/10-VALIDATION.md
|
|
autonomous: true
|
|
requirements: [ADMIN-06]
|
|
estimate:
|
|
tokens: 110000
|
|
raw_tokens: 110000
|
|
tasks: 3
|
|
confidence: low
|
|
must_haves:
|
|
truths:
|
|
- "Per D-23 and the project rule that unit tests close every phase, Vitest suites exist for every module, composable, component and view under admin/src (tests/app, tests/state, tests/shell, tests/list, tests/form, tests/relation, tests/views) with neutral acme fixtures, and npm --prefix admin test passes with no browser e2e dependency."
|
|
- "Every Phase 10 Go change (cabana prefix/cookie/CSRF/options/relation save/messages/toolbar/filters/bundle, boardwalk, phrasebook Forms/Bundle/overrides, bouncer cookie guard, surf prefix collision, swagger2openapi unions) has named tests covering its success and failure branches, and both repositories pass go vet ./... and go test ./..."
|
|
- "TestPhase10AssembledAcceptance proves the four success criteria through the real router at /plytadmin over cookie transport: a limited admin sees only permitted navigation while a developer sees every entry (SC-1); each of Albums, Artists, Collections, Genres and Styles serves its list, form schema, create and update (SC-2); the Collections editors relation searches candidates, links and unlinks (SC-3); every path it calls exists in admin/openapi/admin.json (SC-4)."
|
|
- "scripts/check-phase10.sh --all exits non-zero on any failing stage, a go test run that matches zero tests or skips one, OpenAPI or dist drift, a hygiene violation, or an evidence gap; --self-test proves each of those detectors fails closed."
|
|
- "10-SECURITY-REVIEW.md lists T-10-01 through T-10-25 and T-10-SC with disposition, production mitigation, and the exact test or gate stage that fails when the mitigation is removed; 10-VALIDATION.md maps every plan task to its command with nyquist_compliant true only after the gate passes."
|
|
- statement: "[flagged assumption SC-4] 'No hand-maintained duplicate type' is enforced mechanically: admin/src/api holds only the generated schema.d.ts, the client, and type aliases onto generated schemas; no interface or type literal re-declares an API payload."
|
|
verification: backstop
|
|
artifacts:
|
|
- path: "scripts/check-phase10.sh"
|
|
provides: "Fail-closed Phase 10 gate with self-test, go, security, postgres, spa, openapi, dist, hygiene, evidence and all stages"
|
|
- path: "../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_e2e_test.go"
|
|
provides: "Assembled PostgreSQL acceptance for SC-1 to SC-4"
|
|
- path: "cabana/phase10_coverage_test.go"
|
|
provides: "Remaining branch coverage for Phase 10 cabana changes"
|
|
- path: ".planning/phases/10-admin-vue-spa/10-SECURITY-REVIEW.md"
|
|
provides: "Threat-to-test evidence ledger"
|
|
key_links:
|
|
- from: "scripts/check-phase10.sh"
|
|
to: "go test -json output"
|
|
via: "zero-test, skip and failure detection per stage"
|
|
pattern: "phase10_detect"
|
|
- from: "scripts/check-phase10.sh"
|
|
to: "scripts/check-admin-openapi.sh and scripts/check-admin-dist.sh"
|
|
via: "openapi and dist stages"
|
|
pattern: "check-admin"
|
|
- from: "10-VALIDATION.md"
|
|
to: "10-01..10-05 task verify commands"
|
|
via: "per-task verification map"
|
|
pattern: "10-0"
|
|
prohibitions:
|
|
- "[flagged-unverified] Phase acceptance must not depend on skipped PostgreSQL tests, zero-test runs, or a hand-edited boardwalk/dist or schema.d.ts."
|
|
- "[flagged-unverified] No test or fixture inside summercms.go may use Płytarium or fonoteka names; app names appear only in fonoteka.go tests."
|
|
- "[flagged-unverified] A high threat must not be marked mitigated without naming the executable test or gate stage that fails when the mitigation is removed."
|
|
---
|
|
|
|
## Phase Goal
|
|
|
|
**As a** backend administrator, **I want to** open my project's own admin URL, log in and manage Albums, Artists, Collections, Genres and Styles through schema-driven lists, forms and the relation manager, **so that** I can administer the catalogue from one Go binary without the WinterCMS backend.
|
|
|
|
<objective>
|
|
Close Phase 10 with full unit test coverage (the project rule: unit tests are the last plan), an assembled acceptance test for all four success criteria, and one fail-closed gate script with security evidence.
|
|
|
|
Purpose: Plans 10-01 to 10-04 carried smoke tests only; this plan brings every SPA module and every Go change to named, branch-level tests and makes the phase acceptance a single command. Decisions implemented: D-23 (Vitest plus Go tests, no Playwright), D-04 (drift gates in the phase gate), D-15/D-16 (SC-4 hygiene), D-28 (this is plan 05).
|
|
Output: Vitest suites, Go coverage tests in both repos, `scripts/check-phase10.sh`, `10-SECURITY-REVIEW.md`, finalized `10-VALIDATION.md`.
|
|
|
|
Repos: Task 1 summercms.go; Task 2 summercms.go and fonoteka.go; Task 3 summercms.go (script) plus planning docs (separate docs commit). Never add co-author tags.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@~/.claude/gsd-core/workflows/execute-plan.md
|
|
@~/.claude/gsd-core/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/10-admin-vue-spa/10-CONTEXT.md
|
|
@.planning/phases/10-admin-vue-spa/10-RESEARCH.md
|
|
@.planning/phases/10-admin-vue-spa/10-VALIDATION.md
|
|
@.planning/phases/10-admin-vue-spa/10-01-SUMMARY.md
|
|
@.planning/phases/10-admin-vue-spa/10-02-SUMMARY.md
|
|
@.planning/phases/10-admin-vue-spa/10-03-SUMMARY.md
|
|
@.planning/phases/10-admin-vue-spa/10-04-SUMMARY.md
|
|
@scripts/check-phase9.sh
|
|
@.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-SECURITY-REVIEW.md
|
|
|
|
<interfaces>
|
|
Gate pattern to reuse: `scripts/check-phase9.sh` (`phase9_detect` parses `go test -json` and exits 1 on fail, 2 on a skipped test, 3 on zero passing tests; `phase9_go DIR PKGS -run REGEX`; staged `case` dispatch; `--self-test` feeds synthetic bad runs to the detector).
|
|
Threat IDs in this phase: T-10-01..T-10-08 and T-10-17 (Plan 10-01), T-10-09..T-10-15 (10-02), T-10-16, T-10-18..T-10-20 (10-03), T-10-21..T-10-23 (10-04), T-10-24..T-10-25 (this plan), T-10-SC (all plans).
|
|
</interfaces>
|
|
</context>
|
|
|
|
## Artifacts this phase produces
|
|
|
|
- Vitest suites under `admin/tests/{app,state,shell,list,form,relation,views}` and shared fixtures/setup
|
|
- Go tests: extended `boardwalk/boardwalk_test.go`, `bouncer/cookie_guard_test.go`, `cabana/phase10_coverage_test.go`, extended `phrasebook/phase10_test.go`, extended `surf/admin_prefix_test.go`, `internal/tools/swagger2openapi/main_test.go`, fonoteka `TestPhase10AssembledAcceptance`
|
|
- `scripts/check-phase10.sh` with `--self-test`, `--go`, `--security`, `--postgres`, `--spa`, `--openapi`, `--dist`, `--hygiene`, `--evidence`, `--all`; detector `phase10_detect`
|
|
- `.planning/phases/10-admin-vue-spa/10-SECURITY-REVIEW.md`; finalized `10-VALIDATION.md` (`nyquist_compliant: true`)
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Bring every SPA module, composable and component under Vitest</name>
|
|
<files>admin/vitest.config.ts, admin/tests/setup.ts, admin/tests/fixtures/**, admin/tests/app/i18n.test.ts, admin/tests/app/winterUrl.test.ts, admin/tests/app/listQuery.test.ts, admin/tests/app/runtime.test.ts, admin/tests/app/icons.test.ts, admin/tests/app/client.test.ts, admin/tests/app/router.test.ts, admin/tests/state/useAuth.test.ts, admin/tests/state/useNavigation.test.ts, admin/tests/state/useSidebar.test.ts, admin/tests/state/useToasts.test.ts, admin/tests/state/useSettings.test.ts, admin/tests/shell/AppShell.test.ts, admin/tests/shell/PluginRail.test.ts, admin/tests/shell/SectionPanel.test.ts, admin/tests/shell/SectionFlyout.test.ts, admin/tests/shell/UserMenu.test.ts, admin/tests/shell/Breadcrumbs.test.ts, admin/tests/list/DataTable.test.ts, admin/tests/list/ListToolbar.test.ts, admin/tests/list/FilterBar.test.ts, admin/tests/list/Pagination.test.ts, admin/tests/list/CellValue.test.ts, admin/tests/list/ListView.test.ts, admin/tests/form/registry.test.ts, admin/tests/form/FormGrid.test.ts, admin/tests/form/FormField.test.ts, admin/tests/form/FormTabs.test.ts, admin/tests/form/fields.test.ts, admin/tests/form/RelationField.test.ts, admin/tests/form/FormView.test.ts, admin/tests/form/Settings.test.ts, admin/tests/relation/RelationManager.test.ts, admin/tests/relation/RelationPickerModal.test.ts, admin/tests/views/LoginView.test.ts</files>
|
|
<read_first>admin/src (every module being tested), admin/tests/setup.ts, admin/tests/smoke/*.smoke.test.ts, admin/tests/fixtures/*, .planning/phases/10-admin-vue-spa/design/README.md (States, Interactions), phrasebook/translator.go (interpolate, Choice)</read_first>
|
|
<behavior>
|
|
- app: interpolate parity with phrasebook for :name/:Name/:NAME and overlapping names; plural selection for pl counts 1, 2, 5, 22, 25 and en 1, 2 with fallback to other; t returns the key when missing; mapWinterUrl for empty, create, update/:id, foreign and malformed input; listQuery round-trip including filter[...] and invalid numbers; runtime reads the meta and derives the API base; icons resolve lucide names, Winter aliases and the fallback; client sets X-Requested-With, single-flights refresh for concurrent 401s, replays once, then routes to login with redirect; router guard rejects protocol-relative and absolute redirect values.
|
|
- state: useAuth login/logout/me and proactive refresh timing; useNavigation active plugin and first permitted controller; useSidebar viewport versus persisted manual state and storage key; useToasts queue and auto-dismiss; useSettings visibility.
|
|
- shell, list, form, relation, views: each component's states from the design (selected, empty, empty search, loading, 422, toast, modal, collapsed, flyout, dark) plus its a11y roles and attributes (aria-sort, aria-current, aria-invalid, aria-describedby, aria-required, role=switch, role=tablist/tab, role=dialog with aria-modal, role=listbox with aria-multiselectable, role=menu, role=status, role=alert).
|
|
</behavior>
|
|
<action>Per D-23, write Vitest component and unit tests with @vue/test-utils and happy-dom for every file under `admin/src` (the list in `<files>` is one suite per module; add a suite if a module was added after planning). Mock HTTP by injecting a fetch implementation into the openapi-fetch client from `tests/setup.ts` (never a real network), keep fixtures neutral (`acme.demo.*`, labels like "Widgets", "Members"; no Płytarium words) and shaped from real generated types (fixtures are typed by importing the generated `components` schemas in a typed helper so drift fails typecheck). Reproduce the design's extra shapes as fixtures only (D-08): tabs, switch and checkbox toggle cards, single, multiple and read-only relations, an unknown `colorpicker` type, and all three filter shapes. Assert behaviour and accessibility attributes, not snapshots of markup. Keep the smoke tests. `vitest.config.ts` keeps happy-dom and adds `restoreMocks: true`; no coverage-provider package is added (it would be a new dependency).</action>
|
|
<verify>
|
|
<automated>npm --prefix admin run typecheck && npm --prefix admin test -- tests/app tests/state tests/shell tests/list tests/form tests/relation tests/views tests/smoke</automated>
|
|
<fails_when>Non-zero exit; vitest prints "No test files found" for any listed directory, or any "FAIL" line; vue-tsc reports an error.</fails_when>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- Every `.ts` and `.vue` file under `admin/src` except `main.ts`, `api/schema.d.ts` and `env.d.ts` is imported by at least one test file (checked by the hygiene stage in Task 3).
|
|
- `grep -rniE 'pl[yý]tarium|fonoteka|albumy|kolekcj' admin/tests` prints nothing.
|
|
- `grep -rn 'playwright' admin/package.json` prints nothing.
|
|
</acceptance_criteria>
|
|
<done>The whole SPA has behaviour and accessibility tests that run offline in seconds.</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Cover every Phase 10 Go change and prove the four success criteria end to end</name>
|
|
<files>boardwalk/boardwalk_test.go, bouncer/cookie_guard_test.go, cabana/phase10_coverage_test.go, phrasebook/phase10_test.go, surf/admin_prefix_test.go, internal/tools/swagger2openapi/main_test.go, ../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_e2e_test.go</files>
|
|
<read_first>boardwalk/boardwalk.go, bouncer/jwt.go, cabana/prefix.go, cabana/csrf.go, cabana/auth.go, cabana/http.go, cabana/relation_field.go, cabana/messages.go, cabana/lang.go, cabana/list_schema.go, cabana/filter_schema.go, phrasebook/loader.go, phrasebook/translator.go, surf/router.go, internal/tools/swagger2openapi/main.go, ../fonoteka.go/plugins/golem15/fonoteka/admin_phase09_e2e_test.go, ../fonoteka.go/plugins/golem15/fonoteka/admin_paths_test.go</read_first>
|
|
<behavior>
|
|
- bouncer TestPhase10CookieGuard: the backend guard reads summer_admin when no Bearer is present, Bearer wins when both are, an empty cookie is unauthenticated, a frontend-audience token in the cookie is rejected, a blacklisted jti in the cookie is rejected.
|
|
- boardwalk: HEAD requests, query strings, encoded traversal, index.html requested by name gets the rewritten bytes, prefix with nested segments, extensionless unknown path returns index, unknown extension type falls back to mime.
|
|
- cabana TestPhase10Coverage: every mounted unsafe route (including the options, filters and lang routes added later) is covered by the CSRF walk; options and filters edge cases (empty search, page beyond last, per_page above cap); relation labels for read-only fields; messages defaults for relation schemas; toolbar create omission without a form; bundle fallback to app.fallback_locale; refresh with an expired access token inside the refresh window via cookie.
|
|
- phrasebook: override precedence (override beats plugin beats framework), a new locale via override, Bundle merge order, Forms for every entry shape.
|
|
- surf: the exact prefix path and a deeper path both collide; a sibling path such as /backendx does not.
|
|
- swagger2openapi TestUnionRewrite: jsonScalar and fieldContext become the documented unions and other components are untouched.
|
|
- fonoteka TestPhase10AssembledAcceptance: SC-1 to SC-4 as stated in must_haves.
|
|
</behavior>
|
|
<action>Write the tests in `<behavior>` using the existing Testcontainers PostgreSQL helpers where a database is needed (cabana's helper and fonoteka's `bootDB`/`bootConfig`), never an in-memory substitute for assembled cases. `TestPhase10AssembledAcceptance` seeds a developer-role admin, a limited admin granted only `golem15.fonoteka.access_genres`, a matching frontend user for the Albums collection resolver (Phase 9 D-14), a genre, two artists and a second user; then, through `adminAPI(...)` at `/plytadmin` with cookie login and `X-Requested-With`: compares both admins' `/navigation` (limited sees only Genres under Fonoteka, developer sees every side-menu entry including Collections); for each of the five controllers GETs `schema/list`, the list, `schema/form`, POSTs a record (album with genre and ordered artists) and PUTs an update; on a Collection GETs editors candidates with a search term, links the second user, sees it in the linked list, unlinks it; GETs and PUTs the fonoteka settings; loads `/lang`; logs out and gets 401 with the old cookie; finally asserts every path template it called exists in `admin/openapi/admin.json` of the framework (read via `../summercms.go/admin/openapi/admin.json` from the app test, the app knowing the framework is the allowed direction). Fill any branch the earlier plans left without a named test (extend the listed files only).</action>
|
|
<verify>
|
|
<automated>go vet ./... && go test ./boardwalk ./bouncer ./cabana ./phrasebook ./surf ./internal/tools/swagger2openapi -count=1 && go test ./bouncer ./cabana ./phrasebook ./surf ./internal/tools/swagger2openapi -run '^Test(Phase10CookieGuard|Phase10Coverage|Phase10Forms|Phase10LangOverride|Phase10AdminPrefixCollision|UnionRewrite)$' -count=1 -v && (cd ../fonoteka.go && go vet ./... && go test ./plugins/golem15/fonoteka -run '^TestPhase10AssembledAcceptance$' -count=1 -v && go test ./... -count=1)</automated>
|
|
<fails_when>Any command exits non-zero; the verbose runs lack a "--- PASS" line for TestPhase10CookieGuard, TestPhase10Coverage, TestPhase10Forms, TestPhase10LangOverride, TestPhase10AdminPrefixCollision, TestUnionRewrite or TestPhase10AssembledAcceptance, or print "no tests to run" or a SKIP.</fails_when>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- Each behavior above is a named passing test; TestPhase10AssembledAcceptance runs against real PostgreSQL and exercises SC-1 to SC-4.
|
|
- Both repositories pass `go vet ./...` and `go test ./...`.
|
|
- `grep -rniE 'pl[yý]tarium|fonoteka' boardwalk bouncer cabana phrasebook surf internal/tools --include=*_test.go` prints nothing.
|
|
</acceptance_criteria>
|
|
<done>Every Go change in Phase 10 has branch-level tests, and one assembled test proves the four success criteria through the real router.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: One fail-closed Phase 10 gate plus threat and validation evidence</name>
|
|
<files>scripts/check-phase10.sh, .planning/phases/10-admin-vue-spa/10-SECURITY-REVIEW.md, .planning/phases/10-admin-vue-spa/10-VALIDATION.md</files>
|
|
<read_first>scripts/check-phase9.sh, scripts/check-admin-openapi.sh, scripts/check-admin-dist.sh, .planning/phases/10-admin-vue-spa/10-VALIDATION.md, .planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-SECURITY-REVIEW.md, every 10-0N-PLAN.md threat_model and verify block, every 10-0N-SUMMARY.md</read_first>
|
|
<action>(1) `scripts/check-phase10.sh` (bash, set -euo pipefail, committed with the executable bit), modelled on `scripts/check-phase9.sh`: `phase10_detect` parses `go test -json` (exit 1 fail, 2 skip, 3 zero tests, 4 non-JSON); `phase10_go DIR PKGS -run REGEX` runs it. Stages: `--self-test` (bash -n, detector fails on synthetic fail, skip and zero-test runs, every mode flag present, the hygiene stage fails on a temporary fixture containing a raw-HTML directive in a scratch copy); `--go` (go vet and go test ./... in both repos); `--security` (TestPhase10CookieAuth, TestPhase10CSRF, TestPhase10Prefix, TestPhase10CookieGuard, TestPhase10RelationForgedID, TestPhase10CollectionOwnerReadOnly, TestPhase10Bundle, boardwalk tests, plus `scripts/check-phase9.sh --security`); `--postgres` (TestPhase10TracerSPA, TestPhase10AdminAuth, TestPhase10AlbumRelations, TestPhase10Controllers, TestPhase10AssembledAcceptance through the detector so a skip or zero match fails); `--spa` (`npm --prefix admin ci`, typecheck, `npm --prefix admin test`); `--openapi` (`scripts/check-admin-openapi.sh --check`, TestPhase10OpenAPIConformance, TestPhase09ContractInventory, fonoteka `scripts/check-openapi.sh`); `--dist` (`scripts/check-admin-dist.sh`); `--hygiene` (fails when: summercms.go files outside `.planning` under `admin/src`, `admin/tests`, `admin/openapi`, `boardwalk`, `cabana`, `phrasebook` match Płytarium, fonoteka or Polish catalogue domain words; `admin/src` contains a raw-HTML directive; any file under `admin/src` other than `api/client.ts` calls the fetch API directly; `admin/src/api` holds anything but `schema.d.ts`, `client.ts` and `types.ts`, or `types.ts` declares an interface or object type literal instead of aliases onto generated schemas; `boardwalk/dist` references another origin; an icon namespace import or the deprecated lucide package appears; a route literal for the retired admin prefix appears in Go code other than MintAudience issuer arguments; a `.ts` or `.vue` file under `admin/src` other than `main.ts`, `env.d.ts` and `api/schema.d.ts` is imported by no test); `--evidence` (review lists T-10-01..T-10-25 and T-10-SC, validation has `nyquist_compliant: true`, no pending row, and names ADMIN-06; then runs --security, --postgres and --openapi); `--all` runs every stage in order.
|
|
|
|
(2) `10-SECURITY-REVIEW.md`: a fresh code-and-test review of every threat in the five plans' registers (T-10-01..T-10-25, T-10-SC): threat, severity, disposition, the production mitigation with file references, the exact test or gate stage, the observed result, residual risk. A high threat is marked mitigated only when the named test fails if the protection is removed (state how that was checked). Accepted and transferred threats keep their rationale verbatim from the originating plan.
|
|
|
|
(3) `10-VALIDATION.md`: replace the seeded rows with the actual plan/task IDs and commands from the executed plans, record per-row status from the final gate, fill Wave 0 items, keep the two manual-only rows (visual fidelity; full browser flow) pointing at the human-check blocks of Plans 10-03 and 10-04, and set `nyquist_compliant: true` and `wave_0_complete: true` only after `scripts/check-phase10.sh --all` passes.
|
|
|
|
(4) Run `scripts/check-phase10.sh --all`; commit the script with the code and the two docs in a separate docs commit.</action>
|
|
<verify>
|
|
<automated>scripts/check-phase10.sh --self-test && scripts/check-phase10.sh --all</automated>
|
|
<fails_when>Non-zero exit, or any output line starting with "refuse:"; the final line "phase10 all passed" is absent.</fails_when>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `scripts/check-phase10.sh --all` exits 0 and prints "phase10 all passed".
|
|
- `grep -c 'T-10-' .planning/phases/10-admin-vue-spa/10-SECURITY-REVIEW.md` is at least 26 and every T-10 ID from the five plans appears.
|
|
- `grep -c 'nyquist_compliant: true' .planning/phases/10-admin-vue-spa/10-VALIDATION.md` prints 1 and no table row in it contains "pending".
|
|
</acceptance_criteria>
|
|
<done>Phase 10 has one command that proves everything, and auditable threat and validation evidence.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
## Multi-Source Coverage Audit
|
|
|
|
| Source | Item | Coverage | Plan evidence |
|
|
|--------|------|----------|---------------|
|
|
| GOAL | SPA renders login, permission-gated navigation, lists, forms and the relation manager for the five controllers, typed from the generated OpenAPI document | COVERED | 10-01 tracer (login, nav, typed list); 10-02 contract; 10-03 lists/forms/settings; 10-04 relation manager and shell; 10-05 assembled acceptance |
|
|
| REQ | ADMIN-06 | COVERED | 10-01, 10-02, 10-03, 10-04, 10-05 |
|
|
| GOAL | SC-1 login and permission-filtered navigation | COVERED | 10-01 Tasks 2-3; 10-04 Task 2; TestPhase10AssembledAcceptance |
|
|
| GOAL | SC-2 five controllers list and form | COVERED | 10-02 Task 3 TestPhase10Controllers; 10-03 Tasks 1-3 |
|
|
| GOAL | SC-3 Collections relation manager search/link/unlink | COVERED | 10-04 Task 1; TestPhase10AssembledAcceptance |
|
|
| GOAL | SC-4 generated types, no hand-maintained duplicates | COVERED | 10-01 Task 2 pipeline; 10-02 Task 3 full typing and conformance; 10-05 hygiene stage |
|
|
| CONTEXT | D-01, D-02, D-03, D-04 packaging, prefix, API move, committed dist | COVERED | 10-01 Tasks 2-3; 10-05 dist stage |
|
|
| CONTEXT | D-05 field renderer registry and UnsupportedField | COVERED | 10-03 Tasks 1 and 3; 10-04 Task 1 |
|
|
| CONTEXT | D-06, D-07 design fidelity and stack | COVERED | 10-01 Task 2 tokens/fonts/stack; 10-03; 10-04 Task 2 |
|
|
| CONTEXT | D-08 real YAML only, mock extras as fixtures | COVERED | 10-02 Task 3 TestPhase10Controllers; 10-03 fixtures; 10-05 Task 1 |
|
|
| CONTEXT | D-09 error envelope and 422 mapping | COVERED | 10-03 Task 1 |
|
|
| CONTEXT | D-10 routes from controller IDs, rail grouping, server filtering | COVERED | 10-01 Task 2; 10-04 Task 2 |
|
|
| CONTEXT | D-11 lucide icons, Winter map, fonoteka icons | COVERED | 10-01 Tasks 2-3 |
|
|
| CONTEXT | D-12 format column plain text | COVERED | 10-03 Task 2 |
|
|
| CONTEXT | D-13, D-24 messages and placeholder syntax | COVERED | 10-02 Task 2; 10-03 Task 1 |
|
|
| CONTEXT | D-14 declarative toolbar | COVERED | 10-02 Task 2; 10-03 Task 2 |
|
|
| CONTEXT | D-15, D-16 framework OpenAPI and generic records | COVERED | 10-01 Task 2; 10-02 Task 3 |
|
|
| CONTEXT | D-17, D-18 relation options and save | COVERED | 10-02 Task 1; 10-03 Task 3 |
|
|
| CONTEXT | D-19 cookie transport and CSRF | COVERED | 10-01 Tasks 2-3 |
|
|
| CONTEXT | D-20 backend strings, bundle and overrides | COVERED | 10-02 Task 2; 10-03 Task 1 |
|
|
| CONTEXT | D-21 settings screen | COVERED | 10-03 Task 3 |
|
|
| CONTEXT | D-22, D-27 filter bar and model-backed filter options | COVERED | 10-02 Task 3; 10-03 Task 2 |
|
|
| CONTEXT | D-23 Vitest plus Go tests, no Playwright | COVERED | 10-05 Tasks 1-2 |
|
|
| CONTEXT | D-25 Collections navigation item | COVERED | 10-01 Task 3 |
|
|
| CONTEXT | D-26 read-only owner | COVERED | 10-02 Task 1; 10-03 Task 3 |
|
|
| CONTEXT | D-28 five plans | COVERED | 10-01 to 10-05 |
|
|
| RESEARCH | Gaps 1-9 (backend namespace, fonoteka lang, Collections nav, override layer, placeholder syntax, filter choices, page sizes, recordUrl mapping, reserved segments) | COVERED | 10-02 Task 2; 10-01 Task 3; 10-01 Task 3; 10-02 Task 2; 10-02 Task 2; 10-02 Task 3; 10-03 Task 2; 10-03 Task 1; 10-01 Task 3 |
|
|
| RESEARCH | Pitfalls 1-13 (embed all:, go.mod ignore, api fallback, toolbar error, delete without checkboxes, scoped ids, requiredByDefault, conformance, font subsets, refresh race, fixture names, dist drift, owner FK) | COVERED | 10-01, 10-02, 10-05 as cited in each task |
|
|
| RESEARCH | Package legitimacy audit | COVERED | 10-01 Task 1 blocking-human checkpoint |
|
|
| CONTEXT | Deferred ideas (10.1 extension point, Ctrl+K, badge column, Playwright, user/media navigation) | EXCLUDED | No task implements a deferred item |
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| Test and gate results → phase acceptance | Gate completeness decides whether a vulnerable admin surface can be declared done |
|
|
| Framework repo → app repo boundary | summercms.go must stay free of application knowledge |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|----------|-------------|-----------------|
|
|
| T-10-24 | Repudiation | Phase 10 acceptance evidence | high | mitigate | check-phase10.sh detects zero-test, skipped and failing go runs, drift, hygiene and evidence gaps; --self-test proves each detector fails closed; the review names a failing-when-broken test per high threat. |
|
|
| T-10-25 | Tampering | framework/app boundary and hand-maintained API types | low | mitigate | --hygiene stage fails on app names in summercms.go, non-alias API types, direct fetch calls, raw-HTML directives, foreign origins in dist and untested SPA modules. |
|
|
| T-10-SC | Tampering | npm/Go dependencies | high | mitigate | No new package in this plan (no coverage provider); npm ci against the approved lockfile; swag pinned at v1.16.6. |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
`scripts/check-phase10.sh --all` is the phase acceptance command. It fails on any non-zero stage, zero matched tests, a skipped PostgreSQL test, OpenAPI or dist drift, a hygiene violation, or an evidence gap. The manual-only checks (visual fidelity, full browser flow) are the human-check blocks in Plans 10-03 and 10-04, collected at /gsd-verify-work.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- Every SPA module and every Phase 10 Go change has named tests; both repos are green.
|
|
- TestPhase10AssembledAcceptance proves SC-1 to SC-4 against real PostgreSQL.
|
|
- scripts/check-phase10.sh --all passes; the security review and validation map are complete and truthful.
|
|
- The multi-source audit has no missing GOAL, REQ, RESEARCH or CONTEXT item and no deferred item in scope.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
Create `.planning/phases/10-admin-vue-spa/10-05-SUMMARY.md` when done.
|
|
</output>
|