44 KiB
P5-12 — Template and document controls
Starts from 673cff114546a0976ae01ca9ca0d1086d36fee6a on codex/ui-redesign-phase4, after P5-11's local acceptance. This record covers the approved P5-12 slice: grouping the existing Template/Customize controls into sensible defaults and Advanced disclosure. The Phase 3 CV workspace, preservation ledger, revision readiness, pagination oracle and P5-10 source boundary remain authoritative.
The inventory below was established before substantive changes. This is a control-presentation checkpoint, with final executed validation recorded below. Stop before P5-13; no B07, backend/API/schema, dependency, template/font engine, document geometry, PDF renderer, save protocol or source-model change is included.
Application presentation boundary
The implementation keeps the existing CvCustomizeTab Template and Customize modes, the shared template gallery and the editor's single update/undo/save owner. Named Style and Layout groups expose common accent, heading/body fonts, body/heading size, page size, margins and section/entry spacing. Advanced contains the other existing document controls. Disclosure does not write settings, remount the editor or discard hidden values; supported controls remain keyboard accessible.
Numeric controls replace sliders with named spinbuttons using the existing minimum, maximum and step, with pt/mm units where applicable. A blank value means the existing nullable template inheritance, and each control can independently return to null. Invalid transient text has an associated error and is not sent to the update/save owner; the last valid setting remains active. The field's local draft is only input validation state, not a second document/save model. Undo, history and external value changes resynchronize it. Previously saved in-range fractional values remain visible and are not snapped on mounting or unrelated edits. The interface distinguishes inheritance from explicit overrides rather than presenting a generic fallback number as the template's effective value. The theme response does not contain every typography/spacing default, so no client defaults or API are invented merely to display them.
Sidebar controls use the effective layout: an explicit settings.layout override, otherwise the selected template's existing layout metadata. This exposes the inherited sidebar of Technical, Nordic and Creative without writing an explicit layout. Existing stored sidebar keys outside the six mounted switches remain intact when a displayed key is toggled.
The shared gallery keeps the existing schematic thumbnails, ATS filtering, Pro availability and native Enter/Space selection. The thumbnails illustrate catalog structure; they are not new photorealistic output promises or fidelity evidence. Each card has the template name plus an associated category/description/ATS/Pro accessible description. The selected state has a pressed state and check indicator. Locked cards retain readable text instead of dimming the whole container. Gallery focus uses the Phase 4 shared focus rule rather than a separate colour/ring. Its library/create consumer keeps the same callback; only the editor adds reset confirmation. Template selection requests confirmation only when the existing visual reset would discard overrides; Cancel/Escape leaves settings untouched and returns focus. The existing applyCvTheme reset list and preserved content/document metadata remain the mutation contract below. A same-template selection is included rather than treated as harmless when visual overrides exist.
Setting → control → normalized value → renderer matrix
Sources: frontend types/helpers, existing controls, backend settings/normalization, theme catalog, render model and shared renderer. “Common” and “Advanced” describe the new grouping; saved-only fields remain saved-only. Normalization and render behavior are existing behavior, not new P5-12 capabilities.
| Serialized setting | Control / grouping | Normalized value and null/default behavior | Shared model / renderer effect |
|---|---|---|---|
themeId |
Template gallery, ATS/all filter and selected/Pro indicators | Trim/lowercase; blank → modern. Create/save/preview reject unknown IDs; catalog Resolve itself falls back to Modern |
Selects existing catalog data; no template replacement |
accentColor |
Common: six swatches, named native custom colour input and default reset | Valid #rrggbb → lowercase; invalid/blank → null |
Null → theme accent; configured accent drives existing CSS/derived contrast ink |
headingFont, bodyFont |
Common: template default plus six existing font stacks | Exact allowed stack after trim, otherwise null | Null → each template's own font, including catalog defaults not selectable as explicit overrides |
baseFontSizePt |
Common: nullable numeric control, 7–13pt, step 0.25 | Finite values clamp 7–13; null/nonfinite → null | Null → template body size; explicit value reaches body CSS |
headingSizePt |
Common: nullable numeric control, 9–20pt, step 0.5 | Finite values clamp 9–20; null/nonfinite → null | Null → template heading size; caps-rule treatment uses the existing 0.85 multiplier |
lineHeight |
Advanced: nullable numeric control, 1.1–1.8, step 0.02 | Finite values clamp 1.1–1.8; null/nonfinite → null | Null → template line height |
headingStyle |
Advanced: default, caps-rule, underline, plain, bar | Known lowercase choice or null | Null → template treatment; existing CSS is unchanged |
headerStyle |
Advanced: default, plain, band, centered, kicker | Known lowercase choice or null | Null → template treatment; layout-dependent header behavior remains |
skillsStyle |
Advanced: template default, tags, text, bullets, grouped | Known lowercase choice or null | Null → template skills style; tags/text/bullets apply when the model's skills section is tags |
skillGroups |
Advanced: existing multiline Name: item, item field when grouped is selected or saved groups remain |
Up to 30 nonempty groups; trimmed names/items; case-insensitive distinct items, maximum 100 per group; null stays null | A nonempty group list replaces the existing skills section with skill-groups, independently of skillsStyle; explicit explanation, no silent clearing |
pageSize |
Common: A4 / US Letter | Not choice-normalized; stored strings remain | Case-insensitive Letter → 215.9×279.4mm; other/null → A4 210×297mm |
pageMarginMm |
Common: nullable numeric control, 8–28mm, step 1 | Finite values clamp 8–28; null/nonfinite → null | Null → template margin; effective margin is multiplied by document density |
sectionGapMm |
Common: nullable numeric control, 2–14mm, step 0.5 | Finite values clamp 2–14; null/nonfinite → null | Null → template section gap; existing density multiplier applies |
entryGapMm |
Common: nullable numeric control, 1–10mm, step 0.5 | Finite values clamp 1–10; null/nonfinite → null | Null → template entry gap; existing density/compact treatment applies |
density |
Advanced: compact / balanced / roomy | Not choice-normalized; stored strings remain | Compact 0.82, roomy 1.18, other/null 1.0; affects margins and gaps, not application density |
language |
Advanced: English / Bokmål; existing alias handling | Trim/lowercase, maximum 12 characters; blank → null. UI no/nb/nb-no aliases display nb-NO | Null → English defaults; no/nb/nn prefixes localize default labels, supported date formatting and present wording; authored text is not translated |
dateFormat |
Advanced: keep source format / long / short / numeric / year | Known lowercase choice or null | Parses supported source date strings; formats MMMM yyyy / MMM yyyy / MM/yyyy / yyyy in nb-NO or en-GB. Null or unparsable strings retain source text |
layout |
Advanced: default, single, header-band, sidebar-left, sidebar-right | Known lowercase choice or null | Null → template layout; effective sidebar layout determines existing column split |
sidebarWidthMm |
Advanced: nullable numeric control for effective sidebar layout, 45–85mm, step 1 | Finite values clamp 45–85; null/nonfinite → null | Null → template width, normally 62mm; ignored outside sidebar layouts |
sidebarSections |
Advanced: inherited default named explicitly; Choose sidebar content creates the existing contact/skills/languages seed, then six switches and independent null reset | Trim/lowercase, distinct, maximum 20 keys; null → template list; explicit empty list remains empty | Template default lists can differ and are not all in API metadata, so inheritance is not displayed as a guessed checked list. Arbitrary stored keys survive toggling a displayed key |
showPhoto |
Advanced: existing show-photo switch when exposed by template metadata | Boolean, default false | Resolver includes the current account avatar only when true; renderer also requires a photo and a template shape other than none |
showIcons |
Advanced: existing contact-icons switch when exposed by template metadata | Boolean, default true | Effective icons require both showIcons and template DefaultIcons; true cannot add icons to a template that disables them |
textColor, mutedColor, headingColor, backgroundColor |
Existing saved-only visual overrides; no new controls introduced | Same valid #rrggbb normalization; null → template value |
Applied by renderer. Preserve on unrelated edits and disclose their inclusion in the existing template reset |
showPageNumbers |
Existing saved-only boolean; no new control introduced | Boolean, default false | Stored but not consumed by the renderer; no page-number capability is inferred |
sections[].presentation |
Existing Content section layout control: rows/grid/compact/bubble | Known lowercase choice or null | Null/other → rows; existing grid, compact and bubble CSS; bubble affects suitable bullet/tag containers |
sections[].columns |
Existing Content 1/2 buttons while grid is selected | Only 1/2 retained; invalid → null | Two columns only when presentation is grid and columns is 2; otherwise one |
sections order/hidden/title/items/itemOrder |
Existing Content owner; regression-only here | Existing section fields preserved; item order trims/deduplicates up to 500 stable keys; null items inherits, empty items explicitly empties | Existing canonical resolution, order, omission and CV-only section overrides |
headline, overrides, customSections |
Existing Content/AI owners; regression-only here | Existing nullable wording, stable keys and custom content normalization remain | Selected-CV content lens only; no canonical write or new editor model |
The six permitted font overrides remain Segoe UI/Roboto/Arial, Arial/Helvetica, Georgia/Times New Roman, Helvetica Neue/Arial, Roboto/Arial and Poppins/Segoe UI/Arial using their existing exact serialized stacks. Changing the font choice is not font installation, production font certification or a new brand decision. Numerical steps are UI interaction increments; the backend accepts and clamps finite values without snapping to those steps.
Actual date formatting and grouped-skills behavior
The original readiness record and the model's historical comment describe DateFormat as a frontend hint. That description is incomplete for the current starting code: CvVariantResolver.FormatDate already parses yyyy-MM, yyyy-M, yyyy-MM-dd and yyyy, and emits year/numeric/long/short formats using the document language. Jobs, Education and Projects call that formatter; Certification dates remain source strings. Norwegian_variant_localizes_default_labels_dates_and_keeps_custom_links_clickable already proves year formatting and nå. This record corrects the inventory against current code; it does not rewrite the original evidence, alter its measurements or claim P5-12 implemented date formatting. The old null behavior remains raw source strings, so showing Short in a null control must not be mistaken for a stored/effective Short override.
When the ordinary Skills section survives resolution, nonempty skillGroups changes it to grouped content even after skillsStyle changes away from grouped. Groups do not create a missing, hidden or explicitly emptied Skills section. An empty/null list leaves ordinary source/section skills resolution. The original editor hid groups when another style was selected; P5-12 keeps that existing field reachable while groups remain and explains the precedence. It does not clear content or change the resolver. Changing precedence would require a separately scoped domain decision.
Template IDs, defaults and Pro compatibility
All nine current IDs remain available in the catalog. ATS filtering starts enabled, sorts ATS templates first and retains a selected non-ATS template. Free/Pro availability comes from the server's current role-based entitlements: Premium or Admin has Pro templates. Locked cards retain a named Pro indication and cannot be selected by pointer, Enter or Space.
| ID | Access | Default layout | Header / heading / skills | Photo / default icons |
|---|---|---|---|---|
modern |
Free | header-band | band / caps-rule / tags | rounded / on |
minimal |
Free | single | plain / plain / text | none / off |
ats-classic |
Free | single | plain / caps-rule / bullets | none / off |
code |
Free | single | plain / caps-rule / bullets | none / off |
executive |
Pro | single | centered / underline / text | square / off |
technical |
Pro | sidebar-left | band / bar / bullets | rounded / on |
nordic |
Pro | sidebar-right | plain / caps-rule / tags | circle / on |
elegant |
Pro | single | kicker / underline / tags | circle / off |
creative |
Pro | sidebar-left | band / bar / tags | circle / on |
Generic catalog defaults are body 10pt, heading 12pt, line height 1.42, margin 16mm, section gap 6mm and entry gap 4.5mm. Minimal uses section/entry gaps 7/5mm; Technical uses body 9.5pt and line height 1.36; Elegant uses section gap 7mm; Code uses body 9.4pt, heading 9.5pt, line height 1.43, margin 15.24mm and section/entry gaps 5.6/4.3mm. Code's numbered sections are fixed catalog behavior. Null overrides use these theme values, not the earlier controls' generic displayed fallbacks.
Create rejects unavailable Pro themes. Saving an already-selected Pro template after downgrade remains permitted, while switching into a different unavailable Pro template is rejected. Existing preview/sample rendering is authenticated but not Pro-gated. Gallery locks must not prevent editing an already-saved CV or imply different private/public export authorization. This slice changes none of these policies.
supportedSettings is editor metadata, not a backend capability filter. Most themes advertise accent/typography/spacing/skills/page/heading/header/layout/sidebar/photo/icons. Code advertises only accent/typography/spacing/skills/page. Existing renderer-supported overrides outside that list are not stripped on load, save or render. Some hidden values remain effective, while others are dormant because layout/photo-shape/default-icon behavior suppresses them. Hiding their controls must never be treated as permission to reset them. Missing/empty capability metadata retains the current client fallback that exposes controls; this record does not introduce a stricter compatibility filter.
Exact reset and retained-value contract
applyCvTheme spreads the existing settings, sets the requested themeId, and sets these 20 visual fields to null:
accentColor, headingFont, bodyFont, density,
textColor, mutedColor, headingColor, backgroundColor,
baseFontSizePt, headingSizePt, lineHeight,
pageMarginMm, sectionGapMm, entryGapMm,
layout, headerStyle, headingStyle, skillsStyle,
sidebarWidthMm, sidebarSections
This applies to a different template and to reselecting the same template. At the starting commit the gallery applies it immediately; P5-12 adds an explicit confirmation when defined visual overrides would be discarded. An explicit empty sidebar list is an override, not inheritance. Confirmation describes the reset without claiming compatible overrides are retained by this helper. Cancel makes no change; accepting uses the unchanged helper and existing update transaction. A template selection without visual overrides does not require a destructive-reset confirmation.
The helper retains pageSize, dateFormat, language, showPhoto, showIcons, showPageNumbers, skillGroups, headline, all section order/visibility/title/items/presentation/column settings, item overrides and custom content. Those are not added to the visual reset. Per-control default/reset sets only that nullable field to null and must not alter siblings, source content or disclosure state.
Stored settings serialization and normalization remain unchanged; native API readback exposes inherited settings as explicit nulls. Merely mounting, opening Advanced, changing application theme/locale or hiding an unsupported control does not convert inheritance to explicit generic defaults. Saved-only colours, unsupported/dormant values, arbitrary retained sidebar keys and document language aliases survive unrelated edits, reload and settings-history restore. Unknown saved select values get a named Saved value option instead of being silently replaced.
Renderer, revision and ownership preservation
Unsaved preview, saved/private export and published/current export use the existing current source plus CV settings, resolved through CvVariantService, CvVariantResolver and ThemedCvRenderer. The original corpus remains the same 16 fixtures and 36 preview/PDF pages, with every template default and the accepted risk-based combinations. Equal source/settings must produce unchanged renderer HTML, dimensions, text membership, typography and page counts at the original tolerances. Application control grouping, theme and viewport cannot change the physical document. No hidden-measurement/fragmentation, page padding, frame scaling, PDF flags, shared B03 admission or frozen-file repair is included.
The accepted revision gate remains 5/5: expected-version checks, atomic stale-save/restore rejection, owner lookup and draft recovery. Undo/redo remains mounted-editor settings history. Persisted Restore appends old settings as a new current revision against the current Career Profile; it does not snapshot source/name/publication or expose a historical PDF. Submitted packages separately freeze captured settings/source/rendered content/files. Canonical Career Profile, sibling CV, job associations and submitted-package ownership are regression boundaries only. P2-D01 remains an ordinary required passing native case.
Preservation mapping and application/testing inventory
IDs below use the PRES- prefix from the 57-contract ledger. This is scoped evidence rather than exhaustive certification of every combination in a capability group.
| PRES IDs / touch | Existing behavior | P5-12 presentation | Automated / visual proof |
|---|---|---|---|
| C05 — shared-gallery touch | Library/create template selection and availability | Named cards, readable locks, pressed filter/selection states, shared focus | Inherited library/create/thumbnail tests; new exact pointer/keyboard lock checks; existing create callback unchanged |
| C06 — direct | Five tabs, one preview/save/mobile owner | Style/Layout groups and Advanced disclosure inside current modes | Mounted draft/preview/save/undo retention; disclosure no-write; keyboard/mobile reachability |
| C07 — direct relationship | Section presentation/columns, saved overrides and custom content | Existing Content controls remain reachable; retain values across template/disclosure changes | Exact selected settings, section/item/custom state and dormant fields before/after edit/reset/reload |
| C08 — direct | Nine templates, typography/accent/header/photo/icons/skills/sidebar capabilities | Clear selected/locked/default states; confirmed existing template reset | All IDs/Pro locks, null inheritance, compatible/incompatible controls and same-template reset/Cancel |
| C09 — direct | A4/Letter, document density, spacing, dates, language and columns | Named common/Advanced controls, nullable numeric inputs and resets | Existing ranges/steps/null semantics; actual renderer effect; same-settings output identity |
| C10 — direct regression boundary | Single save queue, dirty guard, undo/redo, restore-as-new-version and revision safety | Same update callback/transactions | Save/failure/conflict/undo/history and unchanged five-case revision gate |
| C12 — direct regression boundary | Private versus published PDF, searchable long output, shared admission | Control presentation only | Original 16/36 fidelity gate, private/public/long/busy/timeout regressions; no new exposure |
| E08 — direct | EN/NB, light/dark, semantic controls/focus/error states | Localized groups, explanations, reset confirmation and selected indication | Four theme/locale representative views; named colour/numeric inputs edited programmatically, keyboard focus/Tab, native select/disclosure/template keyboard checks; expanded scans plus visual review |
| E09 — direct regression boundary | Account disposal and stale-result rejection | No storage/identity owner added | Existing account-switch/B02/late-request tests; no cross-account retained draft |
| C01/C02, W05/W07 — regression-only | Canonical source, linked/sibling CVs and immutable submitted packages | No domain owner change | Native canonical/sibling readback and inherited selected/frozen package/ownership cases |
| C11 — regression-only | Grounded section/whole-document AI review and explicit apply | No AI presentation migration | Existing malformed/late/source-changed/reject/accept tests; stop before P5-13 |
Routes /career/builder/:id, /career/builder, /career/profile and public /cv/:slug, their compatibility entry points and private/public authorization remain unchanged.
| File | Bounded responsibility |
|---|---|
job-tracker-ui/src/components/cv/CvCustomizeTab.tsx |
Existing control owner refactored: groups, Advanced, nullable numeric validation/reset, effective sidebar and confirmed existing theme reset |
job-tracker-ui/src/components/CvTemplateGallery.tsx |
Shared gallery naming/selected indication/locks/focus; library consumer retained |
job-tracker-ui/src/i18n/translations.ts |
19 paired EN/NB group/help/reset/confirmation descriptions; existing Layout label reused |
job-tracker-ui/src/cv-document-controls.test.tsx |
Six new focused behavioural cases, including old saved values, reset/cancel and source-related setting retention |
job-tracker-ui/src/named-controls.test.tsx |
Existing EN/NB controls opened through Advanced with the existing confirmation provider |
job-tracker-ui/src/cv-builder-deep-link.test.tsx |
Existing editor grouping/confirmation expectations updated; exact payload assertions retained |
job-tracker-ui/e2e/cv-settings-presentation.spec.ts |
Four new native theme/locale journeys with persistence/history/source/Pro checks and eight actual preview/PDF exports |
job-tracker-ui/e2e/smoke.spec.ts |
Existing Career Workspace and long Code PDF cases open Advanced before the preserved Skills control; no case removed |
job-tracker-ui/e2e/named-controls-modal-state.spec.ts |
Four inherited theme/locale/viewport journeys keyboard-open Advanced before checking preserved treatment controls; original modal/draft/source assertions remain |
job-tracker-ui/e2e/builder-preview-keyboard.spec.ts |
Existing strict P4-03 journey waits for native scroll completion and the saved/settled document before Ctrl+End; unchanged exact end/start/focus assertions |
docs/redesign/phase-5/p5-12/README.md and evidence/ |
Inventory, preservation mapping, sanitized final results and visual/native output evidence |
The new component fixtures exercise no-write disclosure, null inheritance/independent reset, range errors, old saved values outside interaction increments/capabilities, effective/explicit sidebar controls, destructive template selection and same-template Cancel, exact helper reset fields, retained content/metadata, grouped-skills precedence and pointer/keyboard Pro locks. Extra saved sidebar keys remain retained by the unchanged keyed filter/spread toggle; arbitrary-key combinations are not separately native-qualified by this finite fixture set. Native proof uses actual local endpoints, the final static build, synthetic accounts and source/sibling readback. Component mocks are not claimed as native PDF/package proof. Existing backend create/save/downgraded-template checks remain regression evidence.
Rollback is control grouping/labels/adapters only. Retain serialized names, null/default/normalization semantics, gallery locks, original data/URLs, accepted renderer/revision repairs and tests for required preservation behavior. No stored preference, migration or backend deployment needs unwinding.
Regression-first and review evidence
The initial five focused cases failed against the old controls: named common/Advanced structure was absent, margins used a slider rather than a nullable numeric input, inherited sidebar controls were hidden, template reset had no confirmation and Bokmål scope/selection naming was incomplete. Six final cases protect the boundaries above; the sixth specifically retains legacy 15.24mm, saved-only colours and values outside the currently mounted template controls. Existing editor/control cases were adapted to open Advanced and confirm the unchanged reset payload. No case or assertion was removed to simplify the redesign.
The four new native journeys cover light/English, dark/Bokmål, light/Bokmål and dark/English; free and Premium accounts exercise locked and available Pro templates. They check real persisted values, reload, per-control reset/Undo, template reset Cancel/Escape/focus return, old version restore-as-new-version, unchanged Career Profile and sibling CV, actual PDF download and source-derived document text. Representative paper/typography/geometry changes are measured before and after using the unchanged native oracle below.
Early fixture attempts had three measurement/interaction mistakes, corrected without changing the application contract: the iframe needed its actual localized preview title; native API inheritance reads back as null rather than undefined; a History restore must await the incremented version rather than a possibly still-visible earlier Saved label. Select fixtures now use keyboard open/option activation and assert menu closure/focus return, avoiding a mixed keyboard/mouse race during autosave/reflow. The exact saved-settings and restored-original assertions remain.
A complete candidate run reported 104 passes / eight failures. Two reset checks read the server's previous 22mm before the existing 800ms autosave completed; a still-visible Saved label is not an acknowledgement of the new mutation. Those checks now poll for the exact persisted null, Undo value, selected template and sidebar width. No delay, weaker value assertion or save-owner change is used. An inherited light/English P4-03 case issued Ctrl+End when Page Down had moved only the first 100px and could finish 148px short. That fixture now waits for the actual persisted headline, settled preview and native scrollend, using the pattern already proven in P5-11's new native cases, before retaining the exact end/start/focus checks. It does not scroll through JavaScript or skip the failing case. This extra test-file change is necessary to establish the requested keyboard/fidelity baseline; it is not unrelated application cleanup.
The remaining five failures were four inherited named-controls journeys and the long Code PDF smoke case, which assumed treatment/Skills controls were immediately visible. They now open the approved Advanced disclosure (with keyboard activation/expanded-state proof in the four named-controls cases) and retain every original visible-control, draft, source, modal, structured-output and PDF assertion. No production control is removed or renamed to satisfy a locator. The failed complete run is retained separately from final acceptance.
Final review found that shortening gallery names to the template title lost the previously content-derived ATS/Pro description. Unique aria-describedby associations restore that metadata, with component and native description assertions. The partial browser run was stopped at this finding and is not an accepted full baseline. The frontend production build and complete validation were restarted after this final application change. Screenshot capture disables finite CSS animations so dialog/indicator transitions are not mistaken for settled UI; this changes only evidence capture, not application motion or renderer output.
The new h3 section headings use the existing MUI subtitle2/body2 variants. A candidate using the custom sectionTitle variant inherited a browser serif face; the bounded correction uses existing typography rather than altering the shared theme or introducing a font decision. Final visual review also found that MUI's empty select value hid the inherited-choice text. Shared control selects now display the existing empty/default option with a shrunk visible label; component and native assertions protect that state without writing defaults. Earlier partial runs and the failed full candidate are excluded from final acceptance. Final validation follows the presentation correction and strengthened fixtures. No code changes outside the two existing control/gallery owners and their translations/tests are included.
Final validation and evidence
The inherited P5-11 baseline is 941 backend tests, 434 frontend tests in 80 suites and 108 actual browser passes. No case is annotated as an expected failure; P2-D01 remains required to pass. The only count additions are six component cases in one new suite and four native document-settings journeys. Backend/domain fixtures are unchanged.
| Gate | Inherited | Final result / evidence |
|---|---|---|
| Backend Release regression | 941 | 941 passed; 0 failed/skipped |
| Frontend | 434 / 80 suites | 440 passed / 81 suites; 0 failed/pending |
| Browser | 108 actual passes | 112 actual passes; 0 failed/flaky/skipped; no expected failures |
| Original fidelity | 16 / 36 pages | 16/16; 36 preview pages = 36 PDF pages; unchanged oracle |
| Revision readiness | 5 | 5/5, included in the complete backend suite |
| Added UI-setting fidelity | — | 8/8 before/after exports; 24 preview pages = 24 PDF pages, measured independently from the unchanged original corpus |
| Rate-limit / ingress / PDF admission | 9 / 6 / 1 | 9/9, 6/6 and 1/1 passed sequentially after the full browser server stopped |
| Production config / supply-chain fixtures / Ollama fixtures / extension | 3 / 3 / 5 / 3 | All passed; no count change |
| TypeScript, e2e TypeScript, lint | Passed | Passed after final code/fixture changes |
| Frontend production / API Release builds | Passed | Passed after the final application change; API warnings treated as errors |
| Secret / whitespace / local Markdown targets | Passed | Passed on the final staged candidate; 149 current/root Markdown files have no missing local targets |
The final original fidelity run retains all nine default templates and seven long/layout/boundary combinations. Its maximum absolute marker differences are 0.000004mm horizontal, 1.4931mm vertical, 0.00503pt font size and 0.000046 RGB channel. Declared limits remain page dimensions ±0.5mm, marker positions ±2mm, font size ±0.5pt, colour ±1/255 and vertical safe area ±1mm. Exact page count, content/marker membership and page assignment, hidden-content exclusion and no unexpected blank pages remain hard requirements. PDF is output truth. No failed fixture was removed or tolerance widened.
The extra corpus comes from the actual UI Download PDF action, before and after changing A4 → Letter, margin 16 → 22mm, Arial 10pt → Georgia 11pt, section gap 8mm, entry gap 5mm, line height 1.5, accent #9f1239 and year-format dates. Each of the four theme/locale pairs remains three pages before and three pages after. Physical dimensions change correctly from 210×297mm to 215.9×279.4mm; the effective changed margin is 22mm. The separate settings results retain exact page/content membership at the original oracle tolerances. Maximum absolute marker differences are 0.000002mm horizontal, 0.73794mm vertical, 0.00503pt font size and 0.000042 RGB channel. These 24 extra pages do not replace or alter the original 36-page gate.
Visual, responsive and localisation review
The final static application and local API run on Windows with Chromium 151.0.7922.34 and host timezone Europe/Paris (Romance Standard Time). Four synthetic account journeys cover light/English and light/Bokmål free users, plus dark/English and dark/Bokmål Premium users. The CV route is the existing /career/builder/:id; the long deterministic fixture contains five experience entries with three long bullets each, education, skills, source æøå text and an omitted Interests marker. No production account or credential appears in retained evidence.
The visual manifest retains the captured controls at 1440×900, 768×900 and 320×900, together with common/default controls, invalid margin, all templates, reset confirmation and selected template inheritance. Representative actual images were reviewed after the final application change:
| Review | Evidence / observation |
|---|---|
| Light English desktop common controls | Default controls: common groups, visible inherited font/size text, restrained editor/preview balance |
| Dark Bokmål desktop common controls | Default controls: readable source-scope/help text, translated names, accurate white document canvas |
| Light Bokmål invalid margin | Range error: retained 29mm draft, visible named error, previous valid document remains active |
| Light English and Bokmål intermediate layout | English, Bokmål: two-column fields, long helper wrapping, visible margin focus, reachable Advanced |
| Dark English narrow controls | 320px controls: single-column reflow, readable units/default labels and margin focus |
| Dark Bokmål narrow Advanced | 320px Advanced: disclosure and translated settings reflow without horizontal overflow |
| Locked templates / keyboard focus | Free template gallery: readable Pro indication, schematic cards and shared focus outline; forced/programmatic locked-card focus is a test diagnostic, not a new Tab destination |
| Confirmation and focus | Bokmål dialog: readable reset scope, Cancel focus and distinct apply action; native Escape returns to the originating card |
| Inherited sidebar template | Technical defaults: inheritance remains visibly named, sidebar width reachable through Advanced, original document rendering retained |
| Changed exported document | English first page, Bokmål last page: wrapping, page assignment, source text, accent and section positions agree within declared tolerances |
Native checks retain the named focused margin field during each viewport change, verify no document-level horizontal overflow, and keep Advanced/actions reachable by scrolling. Existing complete-suite cases also exercise 899/900/1024 boundaries and the previous mobile edit/preview and keyboard contracts; the new settings journeys do not independently certify every intermediate width. Captures include current scrolled positions, so earlier fields/title can be outside the viewport under the unchanged sticky shell. This is not a claim that every control stays simultaneously visible, nor full sticky-boundary/reflow qualification.
Keyboard automation proves select open/option activation/closure/focus return, Advanced Enter/expanded state, template Enter/Space and confirmation Escape/focus return. Colour and numeric values are filled programmatically and validated through actual persistence; colour focus/Tab and numeric focus are checked. Native colour-picker operation, spinbutton stepping and keyboard reset activation are not separately manually certified here. No physical touch, software keyboard, human screen-reader, forced-colour, text-spacing or 200%/400% zoom pass is claimed.
Accessibility and performance qualifications
The axe report retains 223 automated scans with zero reported violations, including 12 new scans across common controls, phone Advanced and Template for all four theme/locale pairs. This is not 223 complete accessibility passes: the new scans retain incomplete aria-prohibited-attr checks on the existing labelled Undo/Redo wrapper spans, plus color-contrast checks on controls, units, tabs, shell indicators and preview content. Across the full inherited suite, aria-hidden-focus, aria-valid-attr-value and th-has-data-cells also remain incomplete. The node targets are retained; neither screenshot review nor successful accessible-name assertions converts these rules into passes. Human spoken-name/state-announcement acceptance remains required at P5-15.
Local layout observations compare the same original corpus with P5-11: median native measurement 20.1ms → 22.2ms. The eight new before/after settings layouts range 16.3–47.5ms. These are one observation per fixture on this local run, not statistical improvement, a production benchmark or a keystroke-latency certificate. No material local regression was observed. The existing 300ms preview debounce, rendering-ready signals, 800ms save owner, iframe lifecycle and settings transaction remain unchanged. Opening Advanced does not mutate a version; invalid numeric drafts do not enter the preview/save owner. No continuous whole-document measurement loop or additional font/network provider is introduced.
Backend/revision results, unit results and complete browser results record final acceptance separately from the 104/8 candidate. All 16 renderer HTML hashes match the accepted pagination checkpoint in source hashes. The original measured oracle and raw measurements remain the output-truth gate. The 44 captured UI images are indexed by hash in the visual manifest; the eight new synthetic PDF exports, renderer HTML and first/last comparison images are retained separately. Raw authentication state, cookies, traces and test credentials are excluded.
The accepted complete browser run began at 2026-10-07T21:54:48.388Z and completed in 18.7 minutes, spanning local midnight in Paris. Its 108 inherited cases and four new cases all genuinely pass without retries, skips or expected-failure annotations. P2-D01 is an ordinary passing regression gate. Inherited source/override, stale save/restore/draft, private/public export, submitted/frozen document, permission and keyboard cases remain green.
P5-12 accepted locally within this bounded evidence. All existing template IDs, stored/null/reset semantics, Pro policies, document settings, history/undo/save, canonical isolation and renderer output are preserved. No new P5-13 blocker was found; its AI-review migration has not begun. Broader human accessibility, production fonts/photos, full Phase 5 acceptance and F04 remain unaccepted/open as listed below.
Reproduction commands
These are the executed command shapes with P5-12 artifact destinations. Invoke shell commands through rtk proxy (Git through rtk git) using the installed SDK/bundled runtimes. Before checking the added settings corpus, assemble its four independently emitted pair reports with the standard-library command below; this does not change the oracle or its tolerances. Root commands:
dotnet build JobTrackerApi/JobTrackerApi.csproj --configuration Release --no-restore -warnaserror
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --logger "trx;LogFileName=p5-12.trx" --results-directory .artifacts/p5-12/backend
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --filter FullyQualifiedName~CvRevisionReadinessTests
python -X utf8 scripts/verify-cv-fidelity.py .artifacts/p5-12/fidelity
python -c "import json; from pathlib import Path; p=Path('.artifacts/p5-12/settings-fidelity'); r=[json.loads(f.read_text(encoding='utf-8')) for f in sorted(p.glob('*-measurements.json'))]; assert len(r)==4 and len({x['browserVersion'] for x in r})==1; fixtures=[f for x in r for f in x['fixtures']]; assert len(fixtures)==8 and len({f['fixture'] for f in fixtures})==8; (p/'measurements.json').write_text(json.dumps({'browserVersion':r[0]['browserVersion'],'fixtures':fixtures},ensure_ascii=False,indent=2)+'\n',encoding='utf-8')"
python -X utf8 scripts/verify-cv-fidelity.py .artifacts/p5-12/settings-fidelity
python scripts/test-ingress.py
python scripts/test-supply-chain.py
python scripts/test-ollama-evaluation.py
node browser-extension/tests/capture-url.test.mjs
python scripts/supply-chain.py scan-secrets
python .artifacts/p4-04-check-doc-links.py
git diff --check
git diff --cached --check
UI commands run from job-tracker-ui:
npm test -- --runInBand --silent --json --outputFile=../.artifacts/p5-12/jest.json
npm run typecheck
npm exec tsc -- --project ../.artifacts/p5-09-readiness-e2e-tsconfig.json --noEmit --incremental false
npm run lint
npm run build
npm run test:e2e -- --output=../.artifacts/p5-12/browser --reporter=line,json
npm run test:rate-limits -- --output=../.artifacts/p5-12/rate-limits --reporter=line
npm exec playwright test -- --config=playwright.ingress.config.ts --output=../.artifacts/p5-12/ingress --reporter=line
npm run test:pdf-admission -- --output=../.artifacts/p5-12/pdf-admission --reporter=line
The production build uses NEXT_PUBLIC_API_BASE_URL=http://localhost:5302/api. Browser validation uses installed DOTNET_ROOT/DOTNET_HOST_PATH, PLAYWRIGHT_STATIC_EXPORT=true, PLAYWRIGHT_PREBUILT_EXPORT=true, PLAYWRIGHT_REUSE_SERVERS=false, CV_FIDELITY_OUTPUT=../.artifacts/p5-12/fidelity and PLAYWRIGHT_JSON_OUTPUT_NAME=../.artifacts/p5-12/browser.json. Build after the final application change; use fresh disposable local databases. Specialized suites share port 5302 and run sequentially. Check current local Markdown targets with the inherited ignored checker after final evidence exists; do not commit raw traces, credentials, cookies or authentication state.
Remaining scope and open qualifications
P5-13 owns CV AI review presentation; P5-14 owns additional complete responsive journey acceptance; P5-15 owns Phase 5 acceptance. None begins here. No manual preview zoom/fullscreen, new template capability, production deployment or B07 is introduced.
Keep open: P5-02 human screen-reader acceptance for P5-15; spoken names/announcements; incomplete axe/manual colour-contrast/hidden-focus/table checks; broader pointer/reflow/sticky-boundary qualification; 200%/400% browser zoom, forced colours and text spacing; physical touch/software keyboard/mobile screen reader; production Linux/font substitution, photo/wrapped-line/all-font/all-template/other-browser qualification; F04 production ingress evidence; Dashboard preference-save 500; Board full-result scaling/native touch; deferred followMode compose UI; P5-10-O01 nested drag/drop ownership; and separately recorded inherited timing/focus observations. Automated names, native keyboard checks and local fidelity do not close these operator/production qualifications. This checkpoint must not claim full Phase 5, public accessibility or production acceptance.