24 KiB
P5-09 — CV editor frame and navigation
Implementation starts from efc9e7bdd481a7edf67a9a8d0b545888ee89a670 on codex/ui-redesign-phase4. This checkpoint owns the document frame/navigation only; section forms, preview rendering, template controls, AI review and the final responsive migration remain P5-10–P5-14.
Approved change and preservation mapping
| Contracts | Existing behaviour | Bounded frame change | Required evidence |
|---|---|---|---|
| PRES-C05, PRES-C06 | Named variant, five studio tabs, duplicate/share/PDF | Content-first tabs; compact identity/actions; secondary actions in a named menu | Editor/action regressions; native lifecycle; EN/NB light/dark screenshots |
| PRES-C07 | Ordered, included/omitted and custom sections; pointer drag and move buttons | Section jump navigation opens/focuses the existing form; retained multi-open sections and stable identities | Selection/order/focus/persistence tests; native pointer and keyboard movement |
| PRES-C10 | One save queue, undo/redo, lazy history, restore-as-new-version, conflict recovery | Retain owner/handlers; visible save/version state and editing actions | Complete existing revision/recovery/dirty-guard tests; pending-save tab/resize controls |
| PRES-C08, PRES-C09, PRES-C11, PRES-C12 | Settings, AI review, physical preview and private/public PDF | Same tab consumers and document renderer; no document CSS/geometry changes | All controls/AI tests; 16-fixture fidelity and preview keyboard proof |
| PRES-W05 | Optional job association | Read-only link to the existing associated Job route; no package/tailoring changes | Deep-link and native frozen-package regressions |
| PRES-E07, PRES-E08, PRES-E09 | Existing routing, theme/locale, modal/focus, account boundary | Existing providers/routes, semantic tokens and focus system | Full browser/frontend/security baseline |
PRES-C01/C02 source data and PRES-W07 submitted capture are regression boundaries: no canonical data, revision or submission semantics change. No new save, undo, template or preview state system is introduced.
Implementation decisions
- Keep
CvBuilderEditoras the single editing/save owner. Reorder its existing studio tabs so Content is primary; connect each tab to its corresponding panel. - Keep Content mounted when another studio tab is selected so section navigation/expanded forms retain their editing context. Other advanced consumers retain their existing conditional lifetime.
- Use an ordered, named section selector rather than a permanent third sidebar that would squeeze the existing editor. Existing accordion forms and multi-open behaviour remain; navigation opens/focuses the requested section without editing data.
- Retain the current below-900px Edit/Preview modes and at-1024px split-pane transition. Do not impose the global shell breakpoint on the document workspace.
- Generated PDF remains authoritative. The accepted native-column preview, page geometry, font/layout readiness and fidelity tolerances remain unchanged.
Frame and retained owners
The compact document header contains the editable CV name, This CV only, the acknowledged server version, optional associated-application link, save state and PDF export. Publication is explicitly indicated when enabled. Duplicate, publish/unpublish and copy-link use a named MUI overflow menu and the existing handlers. Escape returns focus to the trigger. Invalid-name/busy conditions and save-before-copy/export/publication behaviour remain in the original owner.
The editing pane has a named h2, session undo/redo, and Content / Template / Customize / AI Tools / History. These are the existing five capabilities, reordered rather than replaced. Each tab identifies its panel. History still loads on selection; restore still confirms and uses the accepted expected-version protocol. The header's version identifies the current settings revision, not an immutable historical Career Profile snapshot. Only submitted packages capture frozen effective data/files; their native proof remains a regression boundary.
Content begins with ordered section navigation. Options use the existing section/custom titles plus explicit Included/Omitted text. Choosing a section opens its existing form, focuses the named disclosure control and scrolls it into view; choosing the same collapsed section opens it again. Focusing any section updates the navigation context. More than one section can remain open. Stable section keys retain editing/focus identity across reordering, and navigation follows the same stored order used by the document. Pointer drag and the existing move-up/down buttons still call useDragReorder/moveItem; no separate document-order model exists.
Keeping Content mounted across studio tabs retains current section/expanded context. Advanced consumers remain conditionally mounted; their fields/settings, source-reset behaviour, AI validators/review and template capabilities are unchanged. No detailed section forms were redesigned. Omitted sections retain readable controls and existing visibility labels; whole-container opacity remains removed as established in P4-01.
The default Content pane adds many legitimate keyboard stops. Native testing reproduced the inherited long-document fixture's 80-Tab traversal ending before the preview. A Go to preview pane-navigation action now follows the section selector on non-compact screens. Keyboard traversal from the studio navigation reaches the selector, then this action; Enter focuses and reveals the existing preview region. The P4-03 fixture now tests that real keyboard path, retaining its focus-contrast, PageDown, Home/End, forward/backward exit, iframe entry, resize and failure/retry assertions. Its mobile path remains unchanged. No numeric traversal allowance was enlarged to hide the problem.
The physical measurement iframe, page iframes, HTML helper, PDF generator, zoom/fit calculation and pagination readiness lifecycle are unchanged. The desktop grid shares available width between editor and preview rather than capping the editor at 480px; preview fit observes that width without changing the physical document geometry. There is no new zoom or print setting.
Responsive, theme and localisation contract
- Below 900px, existing Edit/Preview modes remain. Buttons now expose
aria-pressed; both panes stay mounted and focus capture retains the focused pane across subsequent resizing. - 900–1023px retains the existing stacked editor/preview behaviour; at 1024px the two-pane grid applies. No global shell breakpoint change.
- Native pending-save checks cover 899 / 900 / 1024px without draft/focus loss. Native editing/preview checks cover 768 / 390 / 320px; P4-03 also checks 1440px and preview scroll/exit.
- English and Bokmål labels use the existing translation owner. User-authored names/custom titles remain unchanged. At 320px, native Bokmål axe checks exposed horizontal overflow inside the existing profile-help Alert message beside its action. Its local flex wrapping/message wrapping now allows the action to reflow; no text is removed and no non-interactive text is made focusable to mask the issue.
- Existing MUI/Emotion semantic surfaces, borders, typography and focus-visible treatment are reused. No brand, font, dependency, global theme, shell or route change.
Files and tests
| File | Responsibility |
|---|---|
job-tracker-ui/src/views/CvBuilderEditor.tsx |
Single existing owner; frame/actions/tab panels, section navigation/expanded context, pane entry and local responsive help wrapping |
job-tracker-ui/src/i18n/translations.ts |
Nine matching EN/Bokmål navigation/scope/action labels |
job-tracker-ui/src/cv-builder-deep-link.test.tsx |
Four new behavioural cases; intentional Content-first expectation and explicit Template selection |
job-tracker-ui/e2e/cv-editor-frame.spec.ts |
Four native theme/locale cases, persisted pointer/keyboard order, hidden/custom navigation, pending save through tabs/widths, owner isolation, public revoke/PDF/copy lifecycle and narrow states |
job-tracker-ui/e2e/builder-preview-keyboard.spec.ts |
Named keyboard pane-entry path; inherited scrolling/focus/resize/error checks retained |
job-tracker-ui/e2e/semantic-foundations.spec.ts |
Keyboard traversal explicitly verifies Content → Template → Customize in the approved new order; all focus/contrast/control assertions retained |
job-tracker-ui/e2e/smoke.spec.ts |
Intentional Content-first default; immediate first-disclosure assertion and keyboard activation of the second; both-open/native PDF assertions retained |
This record/evidence, Phase 5 index, Phase 2 trace link and docs/roadmap/todo.md |
Preservation, validation and checkpoint status |
The four new frontend cases protect: omitted/custom section navigation without writes (including reopening and retaining multi-open state across tabs), keyboard move focus/persisted ordered navigation, save-before-duplicate from the secondary menu, and preview pane entry without modifying an unsaved draft. They supplement the existing acknowledged-save queue, restore races, conflict/draft recovery, dirty navigation, undo/redo, section CRUD, controls and AI accept/reject tests.
The new native fixtures use disposable synthetic accounts and the real authenticated API. Only a current save response is deliberately held to test pending editing; profile/CV/job/public/PDF operations and readback are native. Initial fixture corrections (204 preference response, selected MUI combobox accessible name, automatic-save sequencing and a scoped locator typo) are not counted as passing validation. The final full run passes after those corrections and the actual keyboard/narrow-message fixes. Native touch/pointer variants beyond the exercised Chromium drag remain qualified.
The first complete final-code browser attempt recorded 99/102 passing: two inherited focus assertions still assumed Content → Customize adjacency, and the long Code-template fixture failed its two-open-section assertion after consecutive pointer clicks during expansion. The same Code-template case passed 1/1 in isolation without an application change. This does not prove the original timing observation nonexistent. The fixture now asserts the first open state immediately and opens the second disclosure using its existing named keyboard control, retaining both-open assertions and native document checks. Pointer drag remains separately exercised in all four new frame cases; animated disclosure pointer/reflow timing remains qualified for the responsive acceptance slice rather than silently claimed fixed. The complete suite is rerun with the corrected fixtures; no retry/expected-failure marking suppresses a result.
Revision and rendering gates retained
The complete backend suite remains 941/941. The explicit CvRevisionReadinessTests subset remains 5/5, including independent stale save/restore, stale expected-version rejection before history append, settings-history/restore/override reset, and foreign-owner read/history/save/restore denial. Existing frontend queue/race/recovery cases pass; the native browser suite proves concurrent-client rejection, retained/downloadable unsaved content and reviewed reload. The existing native frozen-package/source-change/account archive case, including ordinary passing P2-D01, remains green. This proves the accepted settings/history and immutable submitted-file contract; it does not turn settings revisions into historical Career Profile snapshots.
The unchanged Python oracle compares all 16/16 fixtures, 36 preview pages / 36 native PDF pages, at the original tolerances: exact page count and marker membership; page dimensions ≤0.5mm; marker position ≤2mm; font size ≤0.5pt; RGB component ≤1/255. There are no page-count, membership or measured-placement failures. Maximum measured absolute deviations are x 0.00000369mm, y 1.493055mm, font 0.005026pt, RGB 0.00004510. These tolerances are inherited, not widened for P5-09.
| Fixture | Preview pages | PDF pages | Result |
|---|---|---|---|
| default-modern | 1 | 1 | Pass |
| default-minimal | 1 | 1 | Pass |
| default-executive | 1 | 1 | Pass |
| default-technical | 1 | 1 | Pass |
| default-ats-classic | 1 | 1 | Pass |
| default-nordic | 1 | 1 | Pass |
| default-elegant | 1 | 1 | Pass |
| default-creative | 1 | 1 | Pass |
| default-code | 1 | 1 | Pass |
| long-en-minimal | 4 | 4 | Pass |
| long-nb-code | 4 | 4 | Pass |
| compact-8mm-letter | 4 | 4 | Pass |
| roomy-28mm-nb | 5 | 5 | Pass |
| grid-and-bubbles | 1 | 1 | Pass |
| long-sidebar-nb | 5 | 5 | Pass |
| unbreakable-and-boundary | 4 | 4 | Pass |
Numerical results, native measurements and renderer-source hashes are retained. All 16 render.html sources are byte-identical to the accepted pagination checkpoint. Representative native comparisons are Modern page 1, long English page 4 and 28mm Bokmål page 5. Visual inspection found no new clipping, missing sections or changed pagination. The marker oracle is bounded evidence, not a claim of every wrapped line/photo/font being production-qualified.
The final native measurement durations are 22–71ms, median 41.25ms; the previous checkpoint recorded 22.7–54.1ms, median 40.65ms. These are single local observations, not a benchmark or a performance guarantee. No renderer/update measurement algorithm changed and no additional per-keystroke measurement loop was introduced.
Browser, visual and accessibility evidence
Reviewed on 2026-10-07, using actual rendered Chromium views and synthetic native data at /career/builder/:id. The permanent case summary records 102 actual passes, 20.5 minutes, zero skips/flaky/unexpected/expected-failure cases. 98 inherited cases + 4 P5-09 cases explains the count change. The corrected inherited expectations retain their functional assertions; no case is hidden or renamed to bypass a failure.
| Trace / state | Representative visual evidence | Behavioural oracle |
|---|---|---|
| PRES-C05/C06.frame — identity, essential actions, content/navigation/preview | Light EN, Light NB, Dark EN, Dark NB | Four native frame cases; all five tabs; private/public PDF, publish/revoke and duplicate readback |
| PRES-C07.navigation — omitted/custom sections and persisted order | Omitted Skills, reordered keyboard focus | Selector keyboard entry, native pointer drag, named move controls, native stored order and unchanged canonical profile |
| PRES-C10.save — unsaved/pending/conflict | Unsaved, Saving, native conflict | Existing queue/dirty/conflict tests; held native PUT through tabs/widths; native 409 retains and downloads draft before reviewed reload |
| PRES-C10.history — current revision and existing restoration entry | Light EN history, Dark NB history | Lazy native revision read, five backend integrity cases and existing restore/undo/redo frontend cases |
| PRES-C06/E08.responsive — deliberate modes and reflow | 768px edit, 900px pending, 1024px pending, 320px NB edit | 899/900/1024 draft/focus checks; 768/390/320 no document overflow; all four P4-03 preview keyboard cases |
| PRES-C06/E08/E09.mobile — theme/locale edit and preview | Light EN edit, Light NB preview, Dark EN edit, Dark NB preview | Explicit pressed mode, preserved input, named scroll region, keyboard entry/exit, PageDown/Home/End, iframe entry and focused-pane resizing |
The other EN/NB × light/dark edit/preview/history captures are retained alongside these. Long authored CV/custom section names remain intact; input/select text may truncate in narrow controls with its complete accessible value retained. User-authored content and the CV's document language remain independent from UI language. No data is translated, removed or replaced to fit the frame.
The stable screenshot replay uses an ignored copy of the same four native cases with screenshot-only animations: "disabled", so indicator/menu transitions are captured at their endpoints. A fifth supplemental fixture pauses only the browser clock to capture the existing Unsaved debounce state, then resumes and proves the real persisted save. Supplemental summary: 5/5 passed, separate from the permanent 102-case baseline. Command: npm.cmd run test:e2e -- --config=../.artifacts/p5-09-save-state-capture.config.ts --output=../.artifacts/p5-09/visual-capture --reporter=line,json, using the same local environment below. No app timing, notification lifetime, animations or persistence semantics were changed for capture.
The axe summary preserves 189 scoped scans with zero reported violations, including 12 new-frame scans across populated Content, History and 320px Edit. This does not mean every check passed. New-frame incomplete findings include color-contrast (badge/input/tab/iframe/scroll states) and aria-prohibited-attr on the existing Tooltip-generated Undo/Redo wrapper spans. Across inherited scans, incomplete aria-hidden-focus, aria-valid-attr-value and th-has-data-cells also remain. They are recorded with targets, not reclassified as passes. Buttons retain independent accessible names; existing focus/contrast browser checks and screenshot inspection supplement these results. This is not human screen-reader or native touch certification.
Native keyboard evidence covers studio arrow/Enter behaviour, section selector activation/focus, move controls, overflow Escape/focus return, pane entry/exit, history and mobile modes. Visual review covers useful pane space, readable semantic surfaces/secondary text in both themes, focus visibility, omitted/included indication, long labels and narrow reflow. Detailed section-form hierarchy and broader manual spoken-name/live-announcement acceptance remain later work.
Final validation
Commands below ran against the final application candidate. Shell invocations use the repository's rtk prefix (rtk proxy for runners). Logs, raw browser reports/traces/auth state and native current-run PDFs remain ignored under .artifacts/p5-09; only safe summaries, synthetic visual evidence and measurements are committed.
Browser environment: DOTNET_ROOT=C:/Users/Connor/.dotnet, DOTNET_HOST_PATH=C:/Users/Connor/.dotnet/dotnet.exe, PLAYWRIGHT_STATIC_EXPORT=true, PLAYWRIGHT_PREBUILT_EXPORT=true, CV_FIDELITY_OUTPUT=../.artifacts/p5-09/fidelity, PLAYWRIGHT_JSON_OUTPUT_NAME=../.artifacts/p5-09/browser.json. The test harness supplies disposable development API/data and existing test authentication configuration; no production service/provider was used.
| Command | Result / baseline comparison |
|---|---|
dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-restore --logger "trx;LogFileName=p5-09.trx" --results-directory .artifacts/p5-09/backend |
941/941, zero skipped/failed; unchanged |
npm.cmd test -- --runInBand --silent --json --outputFile=../.artifacts/p5-09/jest.json |
426/426, 80 suites; +4 focused editor behaviours from 422 |
npm.cmd run test:e2e -- --output=../.artifacts/p5-09/browser --reporter=line,json |
102/102 actual, +4 native frame cases from 98; no expected failures/skips/flaky cases |
npm.cmd run typecheck |
Passed |
npm.cmd exec tsc -- --project ../.artifacts/p5-09-readiness-e2e-tsconfig.json --noEmit --incremental false |
All permanent browser fixtures type-check; existing ignored validation config, no CI change |
npm.cmd run lint |
Passed, zero warnings allowed |
npm.cmd run build with NEXT_PUBLIC_API_BASE_URL=http://localhost:5302/api |
Passed, production static export; final recorded rerun exit 0 |
dotnet.exe build JobTrackerApi/JobTrackerApi.csproj --configuration Release --no-restore |
Passed, zero warnings/errors |
dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --filter FullyQualifiedName~CvRevisionReadinessTests |
5/5, included in 941; unchanged revision contract |
npm.cmd run test:rate-limits -- --output=../.artifacts/p5-09/rate-limits --reporter=line |
9/9, unchanged |
npm.cmd exec playwright test -- --config=playwright.ingress.config.ts --output=../.artifacts/p5-09/ingress --reporter=line |
6/6, unchanged; local/application proof only |
npm.cmd run test:pdf-admission -- --output=../.artifacts/p5-09/pdf-admission --reporter=line |
1/1, unchanged |
python scripts/test-ingress.py |
3/3, unchanged configuration/Compose validation |
python scripts/test-supply-chain.py / python scripts/test-ollama-evaluation.py / node browser-extension/tests/capture-url.test.mjs |
3 / 5 / 3 passed, local ancillary fixtures |
Bundled Python -X utf8 scripts/verify-cv-fidelity.py .artifacts/p5-09/fidelity |
16/16, unchanged oracle/tolerances, 36/36 pages |
python scripts/supply-chain.py scan-secrets / git diff --check / git diff --cached --check |
Passed against the final staged candidate; no high-confidence credential patterns or whitespace errors |
python .artifacts/p4-04-check-doc-links.py |
146 current/root Markdown documents, zero missing local file targets; historical archive/external URLs/anchors excluded |
No backend, DTO, renderer, template consumer, provider tree, router, global theme, dependency, package script or CI file changed; a scoped git diff --exit-code against the source checkpoint confirms those owners unchanged. No infrastructure changes were required.
Acceptance and remaining scope
P5-09 frame/navigation accepted locally; stop before P5-10. Revision safety and the measurable rendering gate remain green. No preserved capability is removed and all existing CV tabs/forms/actions remain accessible. This does not claim exhaustive coverage of all 57 contracts, complete CV accessibility or production release acceptance.
- P5-10: detailed section editing, canonical source versus CV-specific override presentation and form hierarchy. Current form owners/data boundaries are retained for that migration.
- P5-11: live-preview presentation; preserve the native pagination/PDF truth contract.
- P5-12/P5-13: template/document-control and grounded AI review presentation. Existing owners and review/accept/reject behaviour remain unchanged here.
- P5-14/P5-15: full responsive/touch/pointer-reflow qualification and final cross-screen acceptance, including human screen-reader spoken names/live/state announcements and incomplete automated findings.
- Production font/photo qualification and F04 production ingress evidence remain open. Local rendering and ingress tests do not prove deployed fonts, photos, host/firewall/DNS/TLS.
- Dashboard preference-save 500, Board full-result scaling/native touch and deferred
followModecompose UI remain separate open debt. P2-D01 remains ordinary passing and is not carried as an expected failure.
No P5-10+, Phase 6–8 or B07 implementation occurred. The implementation checkpoint is the commit containing this record (git log -1 -- docs/redesign/phase-5/p5-09/README.md); remote equality and clean working-tree state are verified after pushing that checkpoint.