docs(10-01): complete admin SPA tracer plan

This commit is contained in:
Jakub Zych
2026-09-27 15:37:02 +02:00
parent dafdb18234
commit e9b48d4720
5 changed files with 376 additions and 18 deletions

View File

@@ -424,13 +424,13 @@ Plans:
3. The Collections form's relation manager lets an admin search, link and unlink an editor. 3. The Collections form's relation manager lets an admin search, link and unlink an editor.
4. API calls in the SPA use TypeScript types generated from the OpenAPI document, with no hand-maintained duplicate type. 4. API calls in the SPA use TypeScript types generated from the OpenAPI document, with no hand-maintained duplicate type.
**Plans**: 5 plans **Plans**: 1/5 plans executed
**UI hint**: yes **UI hint**: yes
Plans: Plans:
**Wave 1** **Wave 1**
- [ ] 10-01-PLAN.md — Tracer: backend.uri prefix, cookie+CSRF transport, embedded SPA (login, navigation, typed Genres list), framework admin OpenAPI pipeline, fonoteka lang/icons/Collections nav - [x] 10-01-PLAN.md — Tracer: backend.uri prefix, cookie+CSRF transport, embedded SPA (login, navigation, typed Genres list), framework admin OpenAPI pipeline, fonoteka lang/icons/Collections nav
**Wave 2** *(blocked on Wave 1 completion)* **Wave 2** *(blocked on Wave 1 completion)*
- [ ] 10-02-PLAN.md — Backend contract: relation options and saves with labels, backend strings bundle and overrides, messages, declarative toolbar, filter options, fully typed OpenAPI with conformance - [ ] 10-02-PLAN.md — Backend contract: relation options and saves with labels, backend strings bundle and overrides, messages, declarative toolbar, filter options, fully typed OpenAPI with conformance
@@ -547,7 +547,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
| 7. User plugin and authentication | 8/8 | Complete | 2026-09-23 | | 7. User plugin and authentication | 8/8 | Complete | 2026-09-23 |
| 8. OAuth2.1 authorization server | 10/10 | Complete | 2026-09-23 | | 8. OAuth2.1 authorization server | 10/10 | Complete | 2026-09-23 |
| 9. Backend admin authentication and schema pipeline | 12/12 | In Progress| | | 9. Backend admin authentication and schema pipeline | 12/12 | In Progress| |
| 10. Admin Vue SPA | 0/TBD | Not started | - | | 10. Admin Vue SPA | 1/5 | In Progress| |
| 11. Jobs, realtime and search infrastructure | 0/TBD | Not started | - | | 11. Jobs, realtime and search infrastructure | 0/TBD | Not started | - |
| 12. Płytarium API — Collections and Albums | 0/TBD | Not started | - | | 12. Płytarium API — Collections and Albums | 0/TBD | Not started | - |
| 13. Płytarium API — wishlist, notifications, CSV, credentials, public routes | 0/TBD | Not started | - | | 13. Płytarium API — wishlist, notifications, CSV, credentials, public routes | 0/TBD | Not started | - |

View File

@@ -4,16 +4,16 @@ milestone: v1.0
current_phase: 10 current_phase: 10
current_phase_name: Admin Vue SPA current_phase_name: Admin Vue SPA
status: executing status: executing
stopped_at: Completed 09-12-PLAN.md stopped_at: Completed 10-01-PLAN.md
last_updated: "2026-09-27T12:11:01.190Z" last_updated: "2026-09-27T13:36:49.057Z"
last_activity: 2026-09-27 last_activity: 2026-09-27
last_activity_desc: Phase 09 plan 12 acceptance gate completed last_activity_desc: Phase 10 plan 01 tracer completed
state_head: 42c7216f5f8ebecbffdfa46e30d744af2590abff state_head: dafdb18234b8be4445ebe05ff96e4cf86cd9e06f
progress: progress:
total_phases: 15 total_phases: 15
completed_phases: 8 completed_phases: 8
total_plans: 72 total_plans: 72
completed_plans: 67 completed_plans: 68
milestone_name: milestone milestone_name: milestone
--- ---
@@ -24,14 +24,14 @@ milestone_name: milestone
See: .planning/PROJECT.md (updated 2026-09-16) See: .planning/PROJECT.md (updated 2026-09-16)
**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. **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 09 — Backend admin authentication and schema pipeline **Current focus:** Phase 10 — Admin Vue SPA
## Current Position ## Current Position
Phase: 10 (Admin Vue SPA) — READY TO EXECUTE Phase: 10 (Admin Vue SPA) — EXECUTING
Plan: 12 of 12 Plan: 2 of 5
Status: Plan complete, verification not yet recorded Status: Ready to execute
Last activity: 2026-09-27 — Phase 09 plan 12 acceptance gate completed Last activity: 2026-09-27 — Phase 10 plan 01 tracer completed
Progress: [██████████] 100% Progress: [██████████] 100%
@@ -120,6 +120,7 @@ Progress: [██████████] 100%
| Phase 09 P09 | 11h 48m | 2 tasks | 9 files | | Phase 09 P09 | 11h 48m | 2 tasks | 9 files |
| Phase 09 P10 | 2h 20m | 3 tasks | 17 files | | Phase 09 P10 | 2h 20m | 3 tasks | 17 files |
| Phase 09 P11 | 1h 15m | 3 tasks | 21 files | | Phase 09 P11 | 1h 15m | 3 tasks | 21 files |
| Phase 10 P01 | 31min | 3 tasks | 111 files |
## Accumulated Context ## Accumulated Context
@@ -310,6 +311,10 @@ Recent decisions affecting current work:
- [Phase 09]: Style slug retains the tracked update-only readonly schema and is generated on create by the model lifecycle - [Phase 09]: Style slug retains the tracked update-only readonly schema and is generated on create by the model lifecycle
- [Phase 09]: Style equal-value list ordering is explicitly stabilized by the shared primary-key tie-breaker - [Phase 09]: Style equal-value list ordering is explicitly stabilized by the shared primary-key tie-breaker
- [Phase 09]: Collection-specific pivot identifiers and stamps remain plugin-owned behind a typed relation contract. — Cabana can validate and execute relations generically without hardcoding Fonoteka tables, columns, roles, or payload behavior. - [Phase 09]: Collection-specific pivot identifiers and stamps remain plugin-owned behind a typed relation contract. — Cabana can validate and execute relations generically without hardcoding Fonoteka tables, columns, roles, or payload behavior.
- [Phase 10]: Admin prefix is backend.uri (default /backend); admin API at {prefix}/api/v1, embedded SPA at {prefix}; fonoteka uses /plytadmin
- [Phase 10]: Admin SPA uses the HttpOnly summer_admin cookie selected by X-Requested-With; cookie-only POST/PUT/DELETE without the header get 403 forbidden; Bearer bodies unchanged
- [Phase 10]: Framework owns admin/openapi/admin.json (swag -> swagger2openapi -> openapi-typescript); fonoteka docs/openapi.json no longer lists admin paths
- [Phase 10]: npm package gate approved: 17 exact pins in admin/package.json; any new package or version needs a checkpoint
### Pending Todos ### Pending Todos
@@ -332,6 +337,6 @@ Items acknowledged and carried forward from previous milestone close:
## Session Continuity ## Session Continuity
Last session: 2026-09-26T21:07:38.615Z Last session: 2026-09-27T13:36:48.754Z
Stopped at: Completed 09-11-PLAN.md Stopped at: Completed 10-01-PLAN.md
Resume file: None Resume file: None

View File

@@ -1,10 +1,10 @@
--- ---
schema_version: 1 schema_version: 1
open_count: 0 open_count: 2
waived_count: 0 waived_count: 0
fixed_count: 1 fixed_count: 1
total_count: 1 total_count: 3
last_updated: 2026-09-24T15:23:23.945Z last_updated: 2026-09-27T13:36:36.179Z
--- ---
# Broken Windows Ledger # Broken Windows Ledger
@@ -16,6 +16,8 @@ last_updated: 2026-09-24T15:23:23.945Z
| id | phase | kind | file | line | description | status | reason | recorded_at | resolved_at | | id | phase | kind | file | line | description | status | reason | recorded_at | resolved_at |
|----|-------|------|------|------|-------------|--------|--------|-------------|-------------| |----|-------|------|------|------|-------------|--------|--------|-------------|-------------|
| 1 | 09 | deviation | plugins/golem15/fonoteka/routes_cors_test.go | | Admin test secret also set on bootConfigWithHTTP and testConfigCORS so router assembly keeps failing closed | fixed | | 2026-09-24T15:22:55.173Z | 2026-09-24T15:23:23.945Z | | 1 | 09 | deviation | plugins/golem15/fonoteka/routes_cors_test.go | | Admin test secret also set on bootConfigWithHTTP and testConfigCORS so router assembly keeps failing closed | fixed | | 2026-09-24T15:22:55.173Z | 2026-09-24T15:23:23.945Z |
| 2 | 10 | stub | admin/src/app/i18n.ts | | t() returns backend::lang keys until the bundle is served (10-02) and loaded (10-03); SPA copy renders raw keys | open | | 2026-09-27T13:36:35.976Z | |
| 3 | 10 | stub | admin/src/views/ListView.vue | | read-only tracer list without search, sort, paging or row links; full list screens in 10-03 | open | | 2026-09-27T13:36:36.179Z | |
````json ````json
[ [
@@ -31,6 +33,32 @@ last_updated: 2026-09-24T15:23:23.945Z
"recorded_at": "2026-09-24T15:22:55.173Z", "recorded_at": "2026-09-24T15:22:55.173Z",
"resolved_at": "2026-09-24T15:23:23.945Z", "resolved_at": "2026-09-24T15:23:23.945Z",
"milestone": "v1.0" "milestone": "v1.0"
},
{
"id": 2,
"kind": "stub",
"phase": "10",
"file": "admin/src/app/i18n.ts",
"line": null,
"description": "t() returns backend::lang keys until the bundle is served (10-02) and loaded (10-03); SPA copy renders raw keys",
"status": "open",
"reason": "",
"recorded_at": "2026-09-27T13:36:35.976Z",
"resolved_at": null,
"milestone": "v1.0"
},
{
"id": 3,
"kind": "stub",
"phase": "10",
"file": "admin/src/views/ListView.vue",
"line": null,
"description": "read-only tracer list without search, sort, paging or row links; full list screens in 10-03",
"status": "open",
"reason": "",
"recorded_at": "2026-09-27T13:36:36.179Z",
"resolved_at": null,
"milestone": "v1.0"
} }
] ]
```` ````

View File

@@ -0,0 +1,307 @@
---
phase: 10-admin-vue-spa
plan: 01
subsystem: admin
tags: [vue, vite, tailwind, openapi-fetch, openapi-typescript, swag, embed, jwt-cookie, csrf]
requires:
- phase: 09-backend-admin-authentication-and-schema-pipeline
provides: cabana admin API (auth, navigation, list schema and list), backend guard, Phase 9 security matrix and OpenAPI annotations
provides:
- backend.uri admin prefix (default /backend) for the admin API ({prefix}/api/v1) and the embedded SPA ({prefix})
- summer_admin HttpOnly cookie transport with CSRF header check; Bearer transport unchanged
- boardwalk package serving the committed boardwalk/dist with one-time index rewrite
- framework-owned admin OpenAPI document admin/openapi/admin.json and generated admin/src/api/schema.d.ts
- admin/ Vite SPA with login, plugin rail, section panel and read-only list
- check-admin-openapi.sh and check-admin-dist.sh drift gates
- fonoteka lang catalog, lucide icons, Collections menu item, /plytadmin mount
affects: [10-02, 10-03, 10-04, 10-05]
actuals:
tokens: 43300 # chars/4 over authored diff additions in both repos; excludes package-lock.json, boardwalk/dist, admin.json, schema.d.ts and docs/openapi.json
tasks: 3 # Task 1 was the approved package gate; Tasks 2 and 3 produced code
commits: 4 # MEASURED: summercms.go 2 (8c3e131..dafdb18) + fonoteka.go 2 (feaca6b..bb4cd7a)
plan_head_before: 8c3e13111183c30d4936c0c14dd06221734f2f4a
plan_head_after: dafdb18234b8be4445ebe05ff96e4cf86cd9e06f
app_plan_head_before: feaca6bdb9761051bb2fa1a8cdb7e90f3986e0e2
app_plan_head_after: bb4cd7a17584fe52f43c9884ec555e319431c12f
tech-stack:
added:
- vue 3.5.35, vue-router 5.1.0, reka-ui 2.9.10, @lucide/vue 1.17.0, openapi-fetch 0.17.0, @fontsource/dm-sans 5.3.0, @fontsource/dm-mono 5.3.0
- dev: vite 7.3.5, @vitejs/plugin-vue 6.0.8, typescript 5.9.3, vue-tsc 3.3.11, tailwindcss 4.3.0, @tailwindcss/vite 4.3.0, vitest 3.2.7, @vue/test-utils 2.4.11, happy-dom 20.11.6, openapi-typescript 7.13.0
- no new Go module requirement (swag runs through go run @v1.16.6)
patterns:
- Path-agnostic SPA build (Vite base ./) plus a boot-time index.html rewrite keyed by a meta token
- Typed generic envelopes (Envelope[T], ListEnvelope[T]) as swag doc types, converted to OpenAPI 3 and fed to openapi-typescript
- Test helper adminAPI(rel) in each repo instead of prefix literals
- Transport selection by X-Requested-With: cookie body without a token, Bearer body unchanged
key-files:
created:
- cabana/prefix.go
- cabana/csrf.go
- cabana/admin_paths_test.go
- cabana/phase10_auth_test.go
- cabana/phase10_csrf_test.go
- boardwalk/boardwalk.go
- boardwalk/boardwalk_test.go
- boardwalk/dist/index.html
- internal/tools/swagger2openapi/main.go
- scripts/check-admin-openapi.sh
- scripts/check-admin-dist.sh
- surf/admin_prefix_test.go
- admin/package.json
- admin/package-lock.json
- admin/openapi/admin.json
- admin/src/api/schema.d.ts
- admin/src/api/client.ts
- admin/src/views/ListView.vue
- admin/tests/smoke/tracer.smoke.test.ts
- ../fonoteka.go/config/backend.yaml
- ../fonoteka.go/plugins/golem15/fonoteka/lang.go
- ../fonoteka.go/plugins/golem15/fonoteka/lang/pl/lang.yaml
- ../fonoteka.go/plugins/golem15/fonoteka/lang/en/lang.yaml
- ../fonoteka.go/plugins/golem15/fonoteka/admin_paths_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_tracer_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_auth_test.go
modified:
- cabana/http.go
- cabana/auth.go
- cabana/admin_openapi.go
- cabana/registry.go
- bouncer/jwt.go
- surf/router.go
- go.mod
- .gitignore
- ../fonoteka.go/config/admin.yaml
- ../fonoteka.go/scripts/check-openapi.sh
- ../fonoteka.go/docs/openapi.json
- ../fonoteka.go/plugins/golem15/fonoteka/admin_navigation.go
- ../fonoteka.go/plugins/golem15/fonoteka/admin_settings.go
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/genre_controller.go
key-decisions:
- "npm package gate (Task 1) approved by the user: the 17 exact pins were installed with npm install --save-exact; any other package or version needs a new checkpoint"
- "The CSRF check wraps each state-changing handler inside the backend guard, as planned: a cookie-only unsafe request is refused before decoding, controller lookup or handler SQL (the guard's user lookup still runs first)"
- "The rail hides a plugin whose side menu is empty on the SPA side; the server keeps its Phase 9 filtering unchanged"
- "i18n t() returns the key until the backend::lang bundle lands (10-02 serves it, 10-03 loads it); SPA copy uses backend::lang.auth.*, nav.*, list.*, page.* keys"
- "The converter rewrites cabana.jsonScalar and cabana.fieldContext into unions so generated TS types are exact"
- "/auth/me now writes the typed AdminProfile struct (same keys, struct field order) so the document and the wire share one type"
patterns-established:
- "Framework admin routes are registered under service.apiBase(); a zero service uses DefaultAdminPrefix"
- "Every SPA call goes through api (openapi-fetch) with X-Requested-With and same-origin credentials; 401 single-flights one refresh and replays once"
requirements-completed: [ADMIN-06]
coverage:
- id: D1
description: "fonoteka serves the embedded SPA at /plytadmin; a developer admin logs in over the cookie, reads navigation and the Genres list, and a cookie-only bulk delete without the header is refused"
requirement: ADMIN-06
verification:
- kind: integration
ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_tracer_test.go#TestPhase10TracerSPA"
status: pass
human_judgment: false
- id: D2
description: "Cookie transport: login and refresh bodies carry no token, Bearer bodies unchanged, cookie refresh rotates and needs the header, logout blacklists and expires the cookie"
requirement: ADMIN-06
verification:
- kind: integration
ref: "cabana/phase10_auth_test.go#TestPhase10CookieAuth"
status: pass
- kind: integration
ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_auth_test.go#TestPhase10AdminAuth"
status: pass
- kind: unit
ref: "cabana/phase10_csrf_test.go#TestPhase10CSRF"
status: pass
human_judgment: false
- id: D3
description: "backend.uri normalization, validation, custom prefix, reserved vendor segments, cookie_secure production guard and plugin route collisions"
requirement: ADMIN-06
verification:
- kind: integration
ref: "cabana/phase10_auth_test.go#TestPhase10Prefix"
status: pass
- kind: unit
ref: "surf/admin_prefix_test.go#TestPhase10AdminPrefixCollision"
status: pass
human_judgment: false
- id: D4
description: "boardwalk serving: index rewrite, missing-token error, embedded assets, api/ delegation, extension 404, traversal, no listing, MIME types, cache and security headers, no inline script"
requirement: ADMIN-06
verification:
- kind: unit
ref: "go test ./boardwalk"
status: pass
human_judgment: false
- id: D5
description: "Framework admin OpenAPI document and generated TS types are committed and drift-checked; committed dist matches a fresh build"
requirement: ADMIN-06
verification:
- kind: other
ref: "scripts/check-admin-openapi.sh --check"
status: pass
- kind: other
ref: "scripts/check-admin-dist.sh"
status: pass
- kind: unit
ref: "cabana/phase09_contract_test.go#TestPhase09ContractInventory"
status: pass
human_judgment: false
- id: D6
description: "SPA smoke: runtime base from meta, login with CSRF header and no stored token, redirect safety, grouped navigation with empty-plugin omission, list columns and rows, single-flight refresh and proactive refresh"
requirement: ADMIN-06
verification:
- kind: unit
ref: "admin/tests/smoke/tracer.smoke.test.ts"
status: pass
human_judgment: false
- id: D7
description: "fonoteka lang catalog resolves every admin key in pl and en; navigation and settings use lucide icons with a Collections item"
requirement: ADMIN-06
verification:
- kind: unit
ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_auth_test.go#TestPhase10LangCatalog"
status: pass
- kind: unit
ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_metadata_test.go#TestAdminMetadataNavigation"
status: pass
human_judgment: false
- id: D8
description: "Visual fidelity of the login screen, rail, section panel and list against design Direction C v2 in a real browser"
verification: []
human_judgment: true
rationale: "No browser e2e in Phase 10 (D-23); component tests assert structure and roles, not rendered appearance"
duration: 31min
completed: 2026-09-27
status: complete
---
# Phase 10 Plan 01: Admin SPA tracer Summary
**The fonoteka binary serves an embedded Vue 3 admin at /plytadmin: cookie login (HttpOnly summer_admin plus X-Requested-With CSRF check), server-filtered navigation and a schema-driven Genres list, all typed from a framework-owned OpenAPI document.**
## Performance
- **Duration:** 31 min
- **Started:** 2026-09-27T13:04:06Z
- **Completed:** 2026-09-27T13:34:49Z
- **Tasks:** 3 (Task 1 package gate approved before this run; Tasks 2 and 3 executed)
- **Files modified:** 86 in summercms.go (41 are generated dist assets, the lockfile and the OpenAPI outputs), 25 in fonoteka.go
## Accomplishments
- Every admin route now lives under `backend.uri` (`{prefix}/api/v1`, default `/backend`, fonoteka `/plytadmin`); the old `/_admin/api/v1` prefix is gone from code and tests and reaches no handler.
- D-19 cookie transport: an `X-Requested-With` login sets `summer_admin` (HttpOnly, Secure, SameSite=Strict, Path=prefix) and returns only `token_type` and `expires_in`. Refresh rotates the cookie. Logout blacklists the jti and expires the cookie. A cookie-only POST, PUT or DELETE without the header gets 403 `forbidden`. Bearer clients keep the Phase 9 bodies.
- `boardwalk` embeds `boardwalk/dist` (`all:dist`) and rewrites `index.html` once per prefix. API misses get the JSON `not_found` envelope, missing files with an extension get a 404, and directories are never listed. Responses carry nosniff, DENY, CSP, Referrer-Policy and noindex.
- The framework owns `admin/openapi/admin.json` with prefix-relative paths and typed envelopes for the six tracer routes. `schema.d.ts` is generated from it and both are drift-checked. fonoteka's parity document no longer lists admin paths.
- The `admin/` SPA covers login, the plugin rail (lucide icons, Winter aliases, neutral fallback, empty plugins hidden), the section panel, the header and a read-only list. It also single-flights refresh on 401, replays the request once and refreshes proactively at 80 percent of `expires_in`.
- Boot guards reject an invalid `backend.uri`, the reserved vendor segments (api, assets, login, settings), non-cabana routes under the prefix, and `backend.cookie_secure: false` in production.
- fonoteka: the PHP lang files are ported to `lang/{pl,en}/lang.yaml` behind `LangFS`, icons are lucide names, Collections sits after Albums, and `blacklist_grace` is 30.
## Task Commits
1. **Task 1: Verify npm package legitimacy** - no commit (blocking-human gate; user replied "approved" for the 17 exact pins)
2. **Task 2: Admin logs in through the embedded SPA and reads the Genres list end to end** - `5f93538` (feat, summercms.go), `d804ca3` (feat, fonoteka.go)
3. **Task 3: Harden the session transport and prefix, and give fonoteka its admin copy and icons** - `dafdb18` (feat, summercms.go), `bb4cd7a` (feat, fonoteka.go)
**Plan metadata:** recorded in the docs commit that adds this file.
Tracer gate (Task 2): the tracer's automated verify was re-run end to end and passed before Task 3 started (human_verify_mode end-of-phase, no human-check).
## Package gate approval (Task 1)
The user approved installing exactly these pins with `npm install --save-exact` in `admin/`: vue 3.5.35, vue-router 5.1.0, reka-ui 2.9.10, @lucide/vue 1.17.0, openapi-fetch 0.17.0, @fontsource/dm-sans 5.3.0, @fontsource/dm-mono 5.3.0; dev: vite 7.3.5, @vitejs/plugin-vue 6.0.8, typescript 5.9.3, vue-tsc 3.3.11, tailwindcss 4.3.0, @tailwindcss/vite 4.3.0, vitest 3.2.7, @vue/test-utils 2.4.11, happy-dom 20.11.6, openapi-typescript 7.13.0. All 17 exist, their repositories match, and none declares an install script. Five are not in the vue-fonoteka-app lockfile (openapi-fetch, both @fontsource fonts, vue-tsc, openapi-typescript), and the user accepted that. No other package was installed. npm 12 blocked two transitive install scripts (esbuild and vue-demi postinstall). Neither was needed: `vite build`, vitest and vue-tsc all run without them.
## Files Created/Modified
- `cabana/prefix.go` - `DefaultAdminPrefix`, `AdminCookieName`, `AdminPrefix(app)` normalization and validation
- `cabana/csrf.go` - `requireAjax` wrapper on every unsafe admin route except login
- `cabana/http.go` - prefix-based mount, SPA routes, boardwalk wiring, cookie_secure, reserved-segment check
- `cabana/auth.go` - cookie transport for login, refresh and logout; typed `AdminProfile`; prefix issuer
- `cabana/admin_openapi.go` - swag general info, `Envelope[T]`, `ListEnvelope[T]`, `AdminRecord`, `AdminProfile`, prefix-relative `@Router`
- `cabana/registry.go` - `checkReservedSegments`
- `bouncer/jwt.go` - `NewBackendJWTGuard(..., cookieNames ...string)`
- `surf/router.go` - `checkAdminPrefix` after `admin.Mount`
- `boardwalk/boardwalk.go` - embedded dist handler
- `internal/tools/swagger2openapi/main.go` - Swagger 2 to OpenAPI 3 converter with union rewrites
- `scripts/check-admin-openapi.sh`, `scripts/check-admin-dist.sh` - generation and drift gates (executable)
- `admin/` - Vite project, generated types, SPA modules and components, fixtures and smoke tests
- `../fonoteka.go/config/backend.yaml`, `config/admin.yaml` - `/plytadmin`, `blacklist_grace: 30`
- `../fonoteka.go/plugins/golem15/fonoteka/lang.go`, `lang/{pl,en}/lang.yaml` - plugin translations
- `../fonoteka.go/plugins/golem15/fonoteka/admin_navigation.go`, `admin_settings.go` - lucide icons, Collections item
## Decisions Made
- The package gate approval is recorded above. Every pin was installed exactly as approved.
- CSRF check placement follows the plan (a handler wrapper applied in `mount`). The backend guard, including its user lookup, still runs before the wrapper on guarded routes. No handler, decoder, controller lookup or handler query runs for a refused request.
- The SPA hides a rail plugin whose side menu is empty. The server's Phase 9 metadata filtering is unchanged.
- `/auth/me` now writes the `AdminProfile` struct. It has the same keys as before, but the JSON key order follows the struct, and the admin API is not a parity surface.
- The OpenAPI converter also turns `cabana.jsonScalar` and `cabana.fieldContext` into unions (research Pattern 4), so the generated TS types match the wire.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Extra internal test file for TestPhase10CSRF**
- **Found during:** Task 3
- **Issue:** The plan puts TestPhase10CookieAuth, TestPhase10CSRF and TestPhase10Prefix in one file. The cookie and prefix tests need `surf.Assemble`, which only an external `cabana_test` file can import (an internal cabana test importing surf is an import cycle). The CSRF spy needs the unexported `service.mount` handlers, so it must be internal.
- **Fix:** `cabana/phase10_auth_test.go` (external) holds CookieAuth and Prefix. `cabana/phase10_csrf_test.go` (internal) holds TestPhase10CSRF, which walks every mounted handler with a body-read spy.
- **Commit:** `dafdb18`
**2. [Rule 3 - Blocking] adminAPI helper duplicated for the external cabana test package**
- **Found during:** Task 2
- **Issue:** `cabana/auth_test.go` and `commands_test.go` are package `cabana_test` and cannot see the internal `admin_paths_test.go` helper.
- **Fix:** Added the same `adminAPI(rel)` helper to `auth_test.go`.
- **Commit:** `5f93538`
**3. [CLAUDE.md - green at every commit] TDD RED kept as verified evidence, not as a failing commit**
- **Found during:** Task 3 (tdd="true")
- **Issue:** The TDD flow commits a failing test first, but CLAUDE.md requires `go vet` and `go test ./...` to be green at every commit, and CLAUDE.md takes precedence.
- **Fix:** All Task 3 tests were written first and run red on the planned assertions: cookie refresh 401, reserved vendor accepted, cookie_secure false accepted in production, prefix collisions accepted, lang keys missing, old icons, and four SPA refresh cases. RED evidence records were verified `RED_EVIDENCE_OK` by `gsd-tools check tdd-red-evidence` for TestPhase10CookieAuth, TestPhase10AdminAuth and the single-flight refresh smoke test. Tests and implementation were then committed together per repository as one logical change. TestPhase10CSRF and the boardwalk tests were already green at RED time, because Task 2 had shipped the CSRF wrapper and the handler.
- **Commits:** `dafdb18`, `bb4cd7a`
**4. [Process] Tracer test written with, not before, the Task 2 implementation**
- **Found during:** Task 2
- **Issue:** The plan says to start with a failing TestPhase10TracerSPA. It was written after the Go and SPA code, so no RED run was captured.
- **Fix:** None needed for correctness. It asserts every step in action item 7 against real PostgreSQL and passes. Recorded here for the verifier.
**5. [Process] Commits land on master**
- **Issue:** The executor's HEAD assertion treats `master` as protected, but this project uses `branching_strategy: none`, and every Phase 1 to 9 plan committed directly to `master` in both repositories. The orchestrator instructed normal commits on the main working tree.
- **Fix:** Committed on `master` in both repositories, following the established project practice.
---
**Total deviations:** 2 auto-fixed (Rule 3), 1 CLAUDE.md-driven, 2 process notes.
**Impact on plan:** None on scope. Test file layout and commit granularity differ slightly from the plan text.
## Issues Encountered
- The fonoteka.go `parity` package already failed before this plan: `TestMigrateSeedsCanonicalGenres` and `TestSchemaMatchesPHPSnapshot` reject the Phase 9 cabana migration history table and the backend tables. The failure reproduces on untouched copies of both repositories at the pre-plan commits. It is logged in `deferred-items.md` and not fixed here. Every other package in both repositories passes `go vet` and `go test ./...`.
## Known Stubs
- `admin/src/app/i18n.ts` - `t()` returns the key until the `backend::lang` bundle is loaded, so the login copy, nav aria labels, list footer and empty or loading texts show `backend::lang.*` keys. This is intentional per the plan: Plan 10-02 serves `GET {prefix}/api/v1/lang` and adds the keys, and Plan 10-03 loads the bundle at startup. Server-resolved labels (navigation, list titles, column labels) already render real text.
- `admin/src/views/ListView.vue` - the list is read-only, with no search, sort, paging controls or row links. It is the tracer scope, and Plan 10-03 builds the full list screens.
## User Setup Required
None. For local development, `SUMMER_ADMIN_DEV_PREFIX` and `SUMMER_ADMIN_DEV_TARGET` configure the Vite dev proxy, and `backend.cookie_secure: false` can be set outside production if a browser rejects Secure cookies on plain http.
## Next Phase Readiness
- Plan 10-02 can grow the contract. The prefix, cookie and CSRF transport, typed envelopes and the OpenAPI pipeline are in place, and `phase09Routes` in `cabana/security_coverage_test.go` is the inventory to extend.
- Plan 10-02 must add every `backend::lang` key the SPA already uses (`auth.title`, `auth.login`, `auth.password`, `auth.invalid`, `auth.submit`, `nav.plugins`, `nav.sections`, `nav.breadcrumbs`, `nav.empty`, `page.not_found`, `list.no_records`, `list.no_results`, `list.loading`, `list.load_failed`, `list.pagination_range`, `list.column_switch_true`, `list.column_switch_false`). Its TestPhase10SPAKeysResolve will check them.
- After any SPA change, run `npm --prefix admin run build` and commit `boardwalk/dist`. `scripts/check-admin-dist.sh` enforces this.
---
*Phase: 10-admin-vue-spa*
*Completed: 2026-09-27*
## Self-Check: PASSED
All created files listed above exist; commits 5f93538 and dafdb18 (summercms.go) and d804ca3 and bb4cd7a (fonoteka.go) are present.

View File

@@ -0,0 +1,18 @@
# Phase 10 deferred items
Out-of-scope issues found while executing Phase 10 plans. They are not caused by
Phase 10 changes and are not fixed here.
## From 10-01
- **fonoteka.go `parity` package fails two tests before Phase 10.**
`TestMigrateSeedsCanonicalGenres` expects no `summer_migrations_summercms_cabana`
history table, and `TestSchemaMatchesPHPSnapshot` reports `backend_users`,
`backend_user_roles` and `backend_jwt_blacklist` as unexpected extra tables.
All four come from the Phase 9 cabana migrations. Reproduced on untouched copies
of both repositories at the pre-10-01 commits (summercms.go `8c3e131`,
fonoteka.go `feaca6b`), so `go test ./...` in fonoteka.go was already red in
this package. Fix belongs to a Phase 9 follow-up: add the cabana history table
and the three backend tables to the parity allow-lists with a reason.
- **`gofmt -l` lists `internal/build/registry.go`** in summercms.go. Pre-existing,
untouched by Phase 10.