docs(10-01): complete admin SPA tracer plan
This commit is contained in:
@@ -424,13 +424,13 @@ Plans:
|
||||
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.
|
||||
|
||||
**Plans**: 5 plans
|
||||
**Plans**: 1/5 plans executed
|
||||
**UI hint**: yes
|
||||
|
||||
Plans:
|
||||
|
||||
**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)*
|
||||
- [ ] 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 |
|
||||
| 8. OAuth2.1 authorization server | 10/10 | Complete | 2026-09-23 |
|
||||
| 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 | - |
|
||||
| 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 | - |
|
||||
|
||||
@@ -4,16 +4,16 @@ milestone: v1.0
|
||||
current_phase: 10
|
||||
current_phase_name: Admin Vue SPA
|
||||
status: executing
|
||||
stopped_at: Completed 09-12-PLAN.md
|
||||
last_updated: "2026-09-27T12:11:01.190Z"
|
||||
stopped_at: Completed 10-01-PLAN.md
|
||||
last_updated: "2026-09-27T13:36:49.057Z"
|
||||
last_activity: 2026-09-27
|
||||
last_activity_desc: Phase 09 plan 12 acceptance gate completed
|
||||
state_head: 42c7216f5f8ebecbffdfa46e30d744af2590abff
|
||||
last_activity_desc: Phase 10 plan 01 tracer completed
|
||||
state_head: dafdb18234b8be4445ebe05ff96e4cf86cd9e06f
|
||||
progress:
|
||||
total_phases: 15
|
||||
completed_phases: 8
|
||||
total_plans: 72
|
||||
completed_plans: 67
|
||||
completed_plans: 68
|
||||
milestone_name: milestone
|
||||
---
|
||||
|
||||
@@ -24,14 +24,14 @@ milestone_name: milestone
|
||||
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.
|
||||
**Current focus:** Phase 09 — Backend admin authentication and schema pipeline
|
||||
**Current focus:** Phase 10 — Admin Vue SPA
|
||||
|
||||
## Current Position
|
||||
|
||||
Phase: 10 (Admin Vue SPA) — READY TO EXECUTE
|
||||
Plan: 12 of 12
|
||||
Status: Plan complete, verification not yet recorded
|
||||
Last activity: 2026-09-27 — Phase 09 plan 12 acceptance gate completed
|
||||
Phase: 10 (Admin Vue SPA) — EXECUTING
|
||||
Plan: 2 of 5
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-09-27 — Phase 10 plan 01 tracer completed
|
||||
|
||||
Progress: [██████████] 100%
|
||||
|
||||
@@ -120,6 +120,7 @@ Progress: [██████████] 100%
|
||||
| Phase 09 P09 | 11h 48m | 2 tasks | 9 files |
|
||||
| Phase 09 P10 | 2h 20m | 3 tasks | 17 files |
|
||||
| Phase 09 P11 | 1h 15m | 3 tasks | 21 files |
|
||||
| Phase 10 P01 | 31min | 3 tasks | 111 files |
|
||||
|
||||
## 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 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 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
|
||||
|
||||
@@ -332,6 +337,6 @@ Items acknowledged and carried forward from previous milestone close:
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-09-26T21:07:38.615Z
|
||||
Stopped at: Completed 09-11-PLAN.md
|
||||
Last session: 2026-09-27T13:36:48.754Z
|
||||
Stopped at: Completed 10-01-PLAN.md
|
||||
Resume file: None
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
schema_version: 1
|
||||
open_count: 0
|
||||
open_count: 2
|
||||
waived_count: 0
|
||||
fixed_count: 1
|
||||
total_count: 1
|
||||
last_updated: 2026-09-24T15:23:23.945Z
|
||||
total_count: 3
|
||||
last_updated: 2026-09-27T13:36:36.179Z
|
||||
---
|
||||
|
||||
# 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 |
|
||||
|----|-------|------|------|------|-------------|--------|--------|-------------|-------------|
|
||||
| 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
|
||||
[
|
||||
@@ -31,6 +33,32 @@ last_updated: 2026-09-24T15:23:23.945Z
|
||||
"recorded_at": "2026-09-24T15:22:55.173Z",
|
||||
"resolved_at": "2026-09-24T15:23:23.945Z",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
````
|
||||
|
||||
307
.planning/phases/10-admin-vue-spa/10-01-SUMMARY.md
Normal file
307
.planning/phases/10-admin-vue-spa/10-01-SUMMARY.md
Normal 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.
|
||||
18
.planning/phases/10-admin-vue-spa/deferred-items.md
Normal file
18
.planning/phases/10-admin-vue-spa/deferred-items.md
Normal 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.
|
||||
Reference in New Issue
Block a user