From 8259a456bb3af7db8f7aedb93e53b3d7f3b2d5f2 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Sun, 27 Sep 2026 17:29:15 +0200 Subject: [PATCH] docs(10-04): complete relation manager and shell polish plan --- .planning/ROADMAP.md | 6 +- .planning/STATE.md | 23 +- .planning/WINDOWS.md | 12 +- .../phases/10-admin-vue-spa/10-04-SUMMARY.md | 276 ++++++++++++++++++ 4 files changed, 299 insertions(+), 18 deletions(-) create mode 100644 .planning/phases/10-admin-vue-spa/10-04-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 2d59184..30e68e9 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -424,7 +424,7 @@ 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**: 3/5 plans executed +**Plans**: 4/5 plans executed **UI hint**: yes Plans: @@ -439,7 +439,7 @@ Plans: - [x] 10-03-PLAN.md — SPA lists, forms, filter bar and settings screen for all five controllers **Wave 4** *(blocked on Wave 3 completion)* -- [ ] 10-04-PLAN.md — Relation manager with picker modal, and shell polish (flyout, collapse, user menu, breadcrumbs, dark mode) +- [x] 10-04-PLAN.md — Relation manager with picker modal, and shell polish (flyout, collapse, user menu, breadcrumbs, dark mode) **Wave 5** *(blocked on Wave 4 completion)* - [ ] 10-05-PLAN.md — Unit tests last: Vitest and Go coverage, assembled acceptance, check-phase10.sh gate and security evidence @@ -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 | 3/5 | In Progress| | +| 10. Admin Vue SPA | 4/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 | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index e9dd82f..f514d72 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -4,16 +4,16 @@ milestone: v1.0 current_phase: 10 current_phase_name: Admin Vue SPA status: executing -stopped_at: Completed 10-03-PLAN.md -last_updated: "2026-09-27T15:02:42.446Z" +stopped_at: Completed 10-04-PLAN.md +last_updated: "2026-09-27T15:28:59.994Z" last_activity: 2026-09-27 -last_activity_desc: Phase 10 plan 03 lists, forms, filters and settings completed -state_head: 1964844eb916a572c7fe8c6e88ed18a46cbf0e77 +last_activity_desc: Phase 10 plan 04 relation manager and shell polish completed +state_head: 445404e394d21c39b855d7a0a672885e49756a00 progress: total_phases: 15 completed_phases: 8 total_plans: 72 - completed_plans: 70 + completed_plans: 71 milestone_name: milestone --- @@ -29,9 +29,9 @@ See: .planning/PROJECT.md (updated 2026-09-16) ## Current Position Phase: 10 (Admin Vue SPA) — EXECUTING -Plan: 4 of 5 +Plan: 5 of 5 Status: Ready to execute -Last activity: 2026-09-27 — Phase 10 plan 03 lists, forms, filters and settings completed +Last activity: 2026-09-27 — Phase 10 plan 04 relation manager and shell polish completed Progress: [██████████] 100% @@ -123,6 +123,7 @@ Progress: [██████████] 100% | Phase 10 P01 | 31min | 3 tasks | 111 files | | Phase 10 P02 | 50min | 3 tasks | 63 files | | Phase 10 P03 | 23 min | 3 tasks | 64 files | +| Phase 10 P04 | 21min | 2 tasks | 42 files | ## Accumulated Context @@ -326,6 +327,10 @@ Recent decisions affecting current work: - [Phase 10]: A form field without span fills the whole row (Winter default); auto and row take the next free slot - [Phase 10]: The save body skips read-only fields and unregistered types (relation-manager, unknown); Winter attributes pass through an allowlist - [Phase 10]: aria-sort reflects only the explicit URL sort; defaultSort is applied server-side without a header indicator +- [Phase 10]: relation-manager is record-bound: dropped on create even without context: update and never sent in the save body +- [Phase 10]: Admin OpenAPI declares search, sort, dir, page and per_page on the linked and candidate relation routes +- [Phase 10]: Collapsed rail: Enter or ArrowDown opens the SectionFlyout and moves focus into it; click still navigates; expand button only for a manual collapse +- [Phase 10]: logout(router) always clears user, navigation and settings and ends on the login route, even when POST /auth/logout fails ### Pending Todos @@ -348,6 +353,6 @@ Items acknowledged and carried forward from previous milestone close: ## Session Continuity -Last session: 2026-09-27T15:02:42.136Z -Stopped at: Completed 10-03-PLAN.md +Last session: 2026-09-27T15:28:59.674Z +Stopped at: Completed 10-04-PLAN.md Resume file: None diff --git a/.planning/WINDOWS.md b/.planning/WINDOWS.md index 2bdcac1..3a2e063 100644 --- a/.planning/WINDOWS.md +++ b/.planning/WINDOWS.md @@ -1,10 +1,10 @@ --- schema_version: 1 -open_count: 1 +open_count: 0 waived_count: 0 -fixed_count: 3 +fixed_count: 4 total_count: 4 -last_updated: 2026-09-27T15:01:17.178Z +last_updated: 2026-09-27T15:27:25.933Z --- # Broken Windows Ledger @@ -18,7 +18,7 @@ last_updated: 2026-09-27T15:01:17.178Z | 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 | fixed | | 2026-09-27T13:36:35.976Z | 2026-09-27T15:01:16.955Z | | 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 | fixed | | 2026-09-27T13:36:36.179Z | 2026-09-27T15:01:17.178Z | -| 4 | 10 | stub | admin/src/components/form/registry.ts | | relation-manager is not registered; the Collections editors tab renders the unsupported-field box until the relation manager lands in 10-04 | open | | 2026-09-27T15:01:12.833Z | | +| 4 | 10 | stub | admin/src/components/form/registry.ts | | relation-manager is not registered; the Collections editors tab renders the unsupported-field box until the relation manager lands in 10-04 | fixed | | 2026-09-27T15:01:12.833Z | 2026-09-27T15:27:25.933Z | ````json [ @@ -68,10 +68,10 @@ last_updated: 2026-09-27T15:01:17.178Z "file": "admin/src/components/form/registry.ts", "line": null, "description": "relation-manager is not registered; the Collections editors tab renders the unsupported-field box until the relation manager lands in 10-04", - "status": "open", + "status": "fixed", "reason": "", "recorded_at": "2026-09-27T15:01:12.833Z", - "resolved_at": null, + "resolved_at": "2026-09-27T15:27:25.933Z", "milestone": "v1.0" } ] diff --git a/.planning/phases/10-admin-vue-spa/10-04-SUMMARY.md b/.planning/phases/10-admin-vue-spa/10-04-SUMMARY.md new file mode 100644 index 0000000..3cd7203 --- /dev/null +++ b/.planning/phases/10-admin-vue-spa/10-04-SUMMARY.md @@ -0,0 +1,276 @@ +--- +phase: 10-admin-vue-spa +plan: 04 +subsystem: admin +tags: [vue, spa, relation-manager, reka-ui, shell, dark-mode, a11y] + +requires: + - phase: 10-admin-vue-spa + plan: 03 + provides: field registry, DataTable, Pagination, ConfirmDialog/useConfirm, toast queue, typed AdminIDsRequest body, backend::lang bundle +provides: + - relation-manager field renderer (RelationManager) with linked list, debounced search, selection, confirmed unlink and toasts + - RelationPickerModal: candidates five per page, selection across pages, Dodaj (N) link, Esc and focus return + - typed search, sort, dir, page and per_page on the linked and candidate relation routes in the admin OpenAPI + - useSidebar (viewport breakpoint plus persisted manual flag), SectionFlyout, collapse and expand buttons + - Breadcrumbs (plugin, controller, record), UserMenu with role and logout, useAuth().logout() + - applyColorScheme (prefers-color-scheme drives .dark), newest-first toasts, dialog and toast motion +affects: [10-05] + +actuals: + tokens: 22100 # chars/4 over added lines in summercms.go; excludes boardwalk/dist, admin.json and schema.d.ts + tasks: 2 + commits: 2 # MEASURED: git rev-list --count 6e1b6dd..445404e (summercms.go only; fonoteka.go untouched) +plan_head_before: 6e1b6dd5bf86984b99cc6224669eb3e714b5bb1a +plan_head_after: 445404e394d21c39b855d7a0a672885e49756a00 + +tech-stack: + added: [] + patterns: + - Record-bound field types (relation-manager) are registered like any control but dropped on create and never enter the save body + - Dialog focus return is explicit: closeAutoFocus is prevented and the parent focuses its opener through a function ref + - Shell UI state that must survive reloads lives in one module (useSidebar) and is the only localStorage user + - Smoke tests run against a desktop, light matchMedia by default and spy on it to simulate narrow or dark screens + +key-files: + created: + - admin/src/components/relation/RelationManager.vue + - admin/src/components/relation/RelationPickerModal.vue + - admin/src/components/shell/SectionFlyout.vue + - admin/src/components/shell/UserMenu.vue + - admin/src/components/shell/Breadcrumbs.vue + - admin/src/state/useSidebar.ts + - admin/src/state/useBreadcrumbs.ts + - admin/src/app/theme.ts + - admin/tests/fixtures/widgets.relation-schema.json + - admin/tests/fixtures/widgets.relation-linked.json + - admin/tests/fixtures/widgets.relation-candidates.json + - admin/tests/smoke/relation.smoke.test.ts + - admin/tests/smoke/shell.smoke.test.ts + modified: + - cabana/admin_openapi.go + - admin/openapi/admin.json + - admin/src/api/schema.d.ts + - admin/src/api/types.ts + - admin/src/components/form/registry.ts + - admin/src/components/form/FormGrid.vue + - admin/src/components/form/FormField.vue + - admin/src/components/form/FieldRenderer.vue + - admin/src/views/FormView.vue + - admin/src/components/list/DataTable.vue + - admin/src/components/shell/AppShell.vue + - admin/src/components/shell/PluginRail.vue + - admin/src/components/shell/SectionPanel.vue + - admin/src/components/ui/Toast.vue + - admin/src/state/useAuth.ts + - admin/src/state/useToasts.ts + - admin/src/main.ts + - admin/src/styles/main.css + - admin/tests/setup.ts + - admin/tests/fixtures/lang.json + - admin/tests/fixtures/widgets.form-schema.json + - admin/tests/smoke/form.smoke.test.ts + - phrasebook/backend/lang/pl/lang.yaml + - phrasebook/backend/lang/en/lang.yaml + - boardwalk/dist/** + +key-decisions: + - "relation-manager is a record-bound type: the form drops it (and its tab) on create even when the YAML has no context: update, and editablePayload never sends it" + - "The admin OpenAPI document declares search, sort, dir, page and per_page on GET {id}/relations/{name} and .../candidates, so the relation manager and picker send typed queries" + - "The picker keeps its selection as a set of ids across pages and search changes and clears it on close; it renders candidates exactly as served (owner exclusion stays server-side, T-10-21)" + - "Focus returns to the link button explicitly: RelationPickerModal prevents Reka's closeAutoFocus and RelationManager focuses the opener through a function ref (a template ref inside v-for is an array)" + - "With the panel collapsed, Enter or ArrowDown on a rail item opens the flyout and moves focus into it instead of navigating; a click still navigates" + - "The expand button shows only for a manual collapse; below 1100px expanding is impossible, so the rail offers the flyout instead" + - "The record crumb comes from the form view through a small useBreadcrumbs module; settings pages show Ustawienia › page label" + - "logout(router) always clears user, navigation and settings and replaces the route with login, even on a failed or thrown call (T-10-23)" + - "The UserMenu root stays mounted when the user clears; only its content reads the user, so logout never tears down a teleport mid-update" + - "Dialog and toast motion is enter-only (200 ms fade plus scale from 0.98), so Reka's Presence never waits on an exit animation" + +patterns-established: + - "Components that need the record id get it as FieldControlProps.recordId, passed through FormGrid, FormField and FieldRenderer" + - "Shell tests reset localStorage, spy on window.matchMedia with a controllable fake and auto-unmount wrappers before the body is cleared" + +requirements-completed: [] # ADMIN-06 stays open until 10-05 (orchestrator instruction) + +coverage: + - id: D1 + description: "Relation manager: absent on create (even without context: update), label, comment, view columns, avatars, 56px rows, toolbar in declared order, debounced search, empty message" + requirement: ADMIN-06 + verification: + - kind: unit + ref: "admin/tests/smoke/relation.smoke.test.ts#relation manager field" + status: pass + human_judgment: false + - id: D2 + description: "Unlink: plural unlinkConfirm, POST unlink with ids, reload, plural unlinked toast; cancel sends nothing" + requirement: ADMIN-06 + verification: + - kind: unit + ref: "admin/tests/smoke/relation.smoke.test.ts#confirms with the plural unlinkConfirm" + status: pass + human_judgment: false + - id: D3 + description: "Picker: role dialog with aria-modal, search autofocus, listbox aria-multiselectable, five per page, range, selection across pages, Dodaj (N) disabled at 0, link body, Esc and Anuluj close with focus return, selection cleared" + requirement: ADMIN-06 + verification: + - kind: unit + ref: "admin/tests/smoke/relation.smoke.test.ts#relation picker modal" + status: pass + human_judgment: false + - id: D4 + description: "Server-side candidate scoping still holds through the prefix (T-10-21 regression)" + requirement: ADMIN-06 + verification: + - kind: integration + ref: "../fonoteka.go/plugins/golem15/fonoteka#TestCollectionsAdmin (15 tests, incl. RelationSchema, Link, Unlink, CrossScope, RelationEdges)" + status: pass + human_judgment: false + - id: D5 + description: "Sidebar: persisted boolean under summer-admin.sidebar, viewport-forced collapse without overwriting it, flyout by keyboard (Enter, ArrowDown), hover and 200 ms mouse-leave, Esc with focus return, no flyout when expanded" + requirement: ADMIN-06 + verification: + - kind: unit + ref: "admin/tests/smoke/shell.smoke.test.ts#collapsible section panel, section flyout" + status: pass + human_judgment: false + - id: D6 + description: "Header: breadcrumbs on record and list routes, user menu initials, name, role and email, logout POSTs /auth/logout and ends on login on success, 500 and network failure" + requirement: ADMIN-06 + verification: + - kind: unit + ref: "admin/tests/smoke/shell.smoke.test.ts#header" + status: pass + human_judgment: false + - id: D7 + description: "Dark class follows a mocked prefers-color-scheme and its changes; newest toast first" + requirement: ADMIN-06 + verification: + - kind: unit + ref: "admin/tests/smoke/shell.smoke.test.ts#theme and toasts" + status: pass + human_judgment: false + - id: D8 + description: "Real link and unlink round trip on a Kolekcje record at /plytadmin, focus return in a browser, visual fidelity at 1280px and about 900px in light and dark mode, limited admin versus superuser menus" + verification: [] + human_judgment: true + rationale: "Both task human-checks: focus management, responsive behaviour and visual fidelity are judged in a browser; D-23 excludes browser e2e" + +duration: 21min +completed: 2026-09-27 +status: complete +--- + +# Phase 10 Plan 04: Relation manager and shell polish Summary + +**An admin editing an existing Collection can now search its editors, add several through a five-per-page picker and remove selected ones, with confirmations and toasts. The shell now follows the design. The section panel collapses to the rail below 1100px or on request, with a keyboard-operable flyout. The header has breadcrumbs and a user menu that shows the role and logs out. Dark mode follows the system.** + +## Performance + +- **Duration:** 21 min +- **Started:** 2026-09-27T15:06:36Z +- **Completed:** 2026-09-27T15:27:25Z +- **Tasks:** 2 +- **Files modified:** 42 in summercms.go (37 excluding rebuilt dist assets); fonoteka.go untouched + +## Accomplishments + +- **Relation manager (D-05, SC-3).** `relation-manager` is registered in the field registry. The form passes the record id down, and the field renders only on an existing record. On create it is dropped along with its tab, even without `context: update`, and it never enters the save body. `RelationManager` loads `schema/relation/{name}` once. Its header shows the label (17px/700), the YAML `comment` as helper text, a 220×38px search (300 ms debounce, resets the page and the selection), and the view's toolbar buttons in declared order. `link` is the primary `user-plus` button; `unlink` is the danger `user-minus` button, disabled without a selection. The linked list reuses `DataTable` in a new relation variant (56px rows, 30px initials avatar, muted secondary columns) inside a 12px bordered container, with sort, pagination from the meta and the relation `empty` message. Unlink confirms with the plural `unlinkConfirm`, POSTs `{ids}`, clears the selection, reloads and toasts the plural `unlinked`. +- **Picker (design screen 5).** The picker is a Reka Dialog with `aria-modal="true"`, a focus trap and Esc to close. It has the title, helper, 34px close button and autofocused 44px search from the relation messages. The candidates form a `role="listbox"` with `aria-multiselectable="true"`, five per page, as 54px option rows (the selected ones get the sel background and a `#e0b020` border). Space or Enter toggles an option and arrow keys move between options. The pager shows `:from–:to z :total`. The selection is a set kept across pages and searches and cleared on close. `Dodaj (N)` is disabled at zero; confirming POSTs `link`, closes, reloads the list and toasts the plural `linked`. Focus returns to the link button. +- **Sidebar (D-06, D-10).** `useSidebar` combines `matchMedia('(max-width: 1099px)')` with a manual flag persisted as `'true'`/`'false'` under `summer-admin.sidebar`. The viewport forces the collapse but never writes the stored choice. `SectionPanel` has its 30px "Zwiń menu" button, and the rail shows the 40px "Rozwiń menu" above Ustawienia after a manual collapse. While the panel is collapsed, `SectionFlyout` (`role="menu"`, 230px, radius 14, shadow `0 16px 40px rgba(20,27,45,.18)`) opens when a rail item is hovered or focused, or on Enter or ArrowDown, which also moves focus into it. Arrow keys, Home and End move between items. It closes on Esc and returns focus to the rail item, and it closes about 200 ms after the pointer or focus leaves. +- **Header.** `Breadcrumbs` shows plugin › controller › record: parent crumbs are muted links, the current crumb is 600 with `aria-current="page"`, and the separators are 14px chevrons. `UserMenu` (Reka DropdownMenu) has the 34px accent initials avatar, and the name and role are hidden below 1100px. Its 280px menu holds the 40px avatar, the bold name, the muted e-mail and Wyloguj. `useAuth().logout(router)` POSTs `/auth/logout`, then clears the user, navigation and settings and replaces the route with login, whatever the call returned. +- **Theme and motion.** `applyColorScheme()` runs before boot and toggles `.dark` on the document element from `prefers-color-scheme`, following changes (A6: no toggle). `.dark` also sets `color-scheme: dark`. The rail stays dark with its `#222b40` border in dark mode. The toast queue shows the newest toast first. Dialogs scale from 0.98 and fade in over 200 ms, backdrops and toasts fade in, and reduced motion turns all of it off. + +## Task Commits + +1. **Task 1 (tracer): search, link and unlink related records through the relation manager:** `f4e97cc` (feat) +2. **Task 2: collapsible panel with flyout, user menu with logout, breadcrumbs and dark mode:** `445404e` (feat) + +**Plan metadata:** recorded in the docs commit that adds this file. + +Tracer gate (Task 1): the whole Task 1 `` passed on the committed tree before Task 2 started (human_verify_mode end-of-phase, auto mode off): typecheck, the relation smoke tests, TestPhase10SPAKeysResolve, check-admin-dist and the five fonoteka Collections relation suites. The human-check moves to phase verification. + +## Decisions Made + +See `key-decisions` in the frontmatter. The two with the widest effect: relation-manager is a record-bound type (never on create, never in the save body), and the admin OpenAPI now types the relation list query. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] The relation list routes had no query parameters in the admin OpenAPI** +- **Found during:** Task 1 +- **Issue:** `schema.d.ts` typed `GET {id}/relations/{name}` and `.../candidates` with `query?: never`, so openapi-fetch could not send search, page or per_page. The plan requires typed calls, and 10-03 rejected SPA-declared shapes. +- **Fix:** `cabana/admin_openapi.go` declares `search`, `sort`, `dir`, `page` and `per_page` on both routes, matching `relationQuery` in `cabana/http.go`. `admin.json` and `schema.d.ts` were regenerated, `check-admin-openapi.sh --check` passes, and handlers are unchanged. `types.ts` gains `RelationQuery`, `RelationPanel` and `RelationColumn` aliases. +- **Commit:** `f4e97cc` + +**2. [Rule 1 - Bug] Registering relation-manager would have put it in the save body** +- **Found during:** Task 1 +- **Issue:** `editablePayload` sends every registered type, so registering the relation manager would have sent `members`/`editors` in create and update bodies. +- **Fix:** A record-bound set in the registry: `isRegistered` excludes it, `needsRecord` drops it on create, and `ownsLabel` lets the component render its own heading. The existing edit smoke test still asserts the exact PUT body. +- **Commit:** `f4e97cc` + +**3. [Rule 2 - Correctness] Relation manager dropped on create even without `context: update`** +- The design requires the tab to render only on an existing record. The form now filters record-bound types in create mode itself instead of relying on the YAML context. The relation smoke fixture omits the context to prove it. +- **Commit:** `f4e97cc` + +**4. [Rule 1] Form smoke test and fixture updated for the real renderer** +- **Issue:** The 10-03 form test asserted the unsupported box on the relation-manager tab. +- **Fix:** The form fixture's relation manager is now the neutral `members` relation (tab Members, with a comment and no context). The test asserts `[data-relation-manager]` and no unsupported box. +- **Commit:** `f4e97cc` + +**5. [Rule 1] Test harness teardown with teleported menus** +- **Found during:** Task 2 +- **Issue:** Each smoke file clears `document.body` after a test while the app is still mounted. The next test's `beforeEach` (`clearUser()`) then unmounted the UserMenu's DropdownMenu teleport inside the detached body. That caused 80 unhandled rejections and a failing vitest exit, although every assertion passed. +- **Fix:** The UserMenu root no longer depends on the user (the trigger is disabled without one). The shell test also auto-unmounts wrappers before its body cleanup. The full smoke run exits 0 with no unhandled errors. +- **Commit:** `445404e` + +**6. [Rule 3] Supporting files not named in the plan** +- `admin/src/state/useBreadcrumbs.ts` carries the record title from FormView to the header. +- `admin/tests/setup.ts` gives every test a desktop, light `matchMedia`, because happy-dom's 1024px default viewport is below the 1100px breakpoint and would collapse the panel in the older tests. +- `FormGrid`, `FormField`, `FieldRenderer` and `api/types.ts` pass `recordId` and carry the relation types. +- **Commits:** `f4e97cc`, `445404e` + +**7. [Rule 1] Acceptance grep for the registry** +- The acceptance check expects one `'relation-manager'` literal in `registry.ts`. The type is now the exported `RELATION_MANAGER` constant, and `FormGrid` uses it too. This Task 1 file change landed in the Task 2 commit. +- **Commit:** `445404e` + +**8. [Scope note] Lang keys** +- Task 1 added `backend::lang.relation.no_candidates` and `backend::lang.relation.load_failed` (pl and en). Task 2 needed no new keys because `nav.collapse`, `nav.expand`, `nav.user_menu`, `nav.breadcrumbs` and `auth.logout` already existed from 10-01. Only the two new keys were inserted into `tests/fixtures/lang.json`, so its existing plural ordering is kept. + +**9. [Process] Not a TDD plan; commits on master** +- The tasks carry no `tdd="true"`. Each task's tests and code were committed together, keeping every commit green. As directed, commits land on `master` (`branching_strategy: none`). + +--- + +**Total deviations:** 7 auto-fixed (2 blocking, 4 bugs, 1 correctness), 2 notes. +**Impact on plan:** None on scope. The OpenAPI change documents existing query parameters only. + +## Issues Encountered + +- The admin bundle grew from 229 kB to 297 kB (gzip 76 to 97 kB). Reka's DropdownMenu brings its popper (floating-ui), and the plan names DropdownMenu for the user menu. No new package was added and the lockfile is unchanged. +- The known fonoteka.go `parity` failures (`TestMigrateSeedsCanonicalGenres`, `TestSchemaMatchesPHPSnapshot`) and `gofmt -l internal/build/registry.go` remain, as logged in `deferred-items.md`. `go vet ./...` and `go test ./...` pass in summercms.go, and the full `TestCollectionsAdmin` family (15 tests) passes in the fonoteka plugin module. + +## Known Stubs + +None. WINDOWS entry 4 (relation-manager not registered) is marked fixed by this plan. + +## Threat Flags + +None. No new endpoint, auth path or storage beyond the plan. The OpenAPI change documents existing query parameters. `localStorage` holds only the sidebar boolean (T-10-22 grep is empty). Candidates are rendered as served, with no client-side filtering (T-10-21). Logout clears local state and leaves the shell even on failure (T-10-23). + +## User Setup Required + +None. + +## Next Phase Readiness + +- Plan 10-05 (unit tests) can target `useSidebar.ts`, `theme.ts`, `useBreadcrumbs.ts`, `useAuth.logout`, the registry's `needsRecord`/`isRegistered` and `editablePayload` directly. The smoke tests already cover the component behaviour. +- The human browser checks of both tasks are left for phase verification: the Kolekcje editors round trip, focus return, and fidelity at 1280px and about 900px in light and dark mode for a limited admin and a superuser. + +--- +*Phase: 10-admin-vue-spa* +*Completed: 2026-09-27* + +## Self-Check: PASSED + +All 13 created files listed above exist; commits f4e97cc and 445404e (summercms.go) are present. Plan verification re-run: typecheck, all 76 smoke tests (exit 0, no unhandled errors), go test ./phrasebook, check-admin-dist.sh, check-admin-openapi.sh --check, go vet ./... and go test ./... pass; the fonoteka TestCollectionsAdmin family (15 tests) passes.