Files
jobtrackingapp/docs/redesign/phase-5/p5-09-pagination-readiness/README.md
T

155 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CV pagination-fidelity repair checkpoint
Source: `046dc304aa68d67e15a019057f9bdec0c89bf475`. This checkpoint repairs only the live document's fragmentation and measurement lifecycle. No P5-09 UI redesign, template/export change, revision change, AI workflow, responsive redesign or B07.
The final decision and executed baseline are recorded below. The original [16-fixture evidence](../p5-09-readiness/README.md#measured-corpus-and-results) remains unchanged; its six failures are not removed or reclassified as passes.
## Root cause and per-fixture diagnosis
The former screen preview added padding to a tall continuous `.page`, estimated keep-together gaps for selected blocks and cropped every physical page by a vertical transform. It explicitly skipped blocks taller than the usable page. That meant flowing entries/list items received neither native line fragmentation nor repeated page margins. Padding an entire section could not implement `widows`, `orphans`, keep-with-next or margin treatment at a fragment boundary. Font readiness was also absent from the parent page-count callback.
All six failures share this mechanism. The sixth is not stale data, reordered DOM or hidden content: it reproduces offline from the identical saved HTML, with stable fonts and no API writes. Its E03/B07 markers appeared on preview page 1 instead of PDF page 2; later S09/R09/R10 positions differed by approximately 21 mm despite equal overall counts.
| Original failing fixture | Original divergence | Repair proof |
|---|---|---|
| long-en-minimal | 3 vs 4 pages; E05/B13/B14 stayed on page 2 instead of 3; later vertical error up to 72.60 mm | Native flow fragmentation, repeated usable height and unchanged print keep rules |
| long-nb-code | 3 vs 4 pages; E03/B07 wrongly on page 1; accumulated subsequent-page displacement | Same native flow; monospaced headings and Segoe body metrics preserved |
| compact-8mm-letter | 3 vs 4 pages; compact flow and page-boundary splitting | Native fragmentation **and print fragmentainer pixel rounding**, independently demonstrated with a native ruler |
| roomy-28mm-nb | 4 vs 5 pages; large repeated safe area absent from long flow; displacement over 200 mm | Renderer margin 33.04 mm retained on every fragment |
| long-sidebar-nb | 4 vs 5 pages; main/sidebar fragmentation and subsequent-page offsets diverged | Existing grid/column layout fragments natively; no sidebar/template rewrite |
| unbreakable-and-boundary | 4 vs 4 pages, but markers assigned to wrong pages and custom-section anchors displaced | Same source and native keep/flow rules; focused marker-membership regression plus positional oracle |
An offline probe reusing actual server HTML and native PDFs corrected all counts and 15/16 positional results with browser column fragmentation. Compact Letter still misplaced later anchors by roughly 10.9 mm when given a fractional fragmentainer height. Rounding each margin separately produced a different wrong result: the Languages section incorrectly fit on page 3. Neither workaround was shipped.
A separate Chromium CLI **1px native ruler**, with the same page and margin rules, establishes the correct physical rounding:
| Page / effective margin | Calculated usable CSS height | Native first-page 1px rows | Preview fragmentainer |
|---|---:|---:|---:|
| A4 / 16 mm | 1001.574803 px | 1002 | 1002 px |
| Letter / 6.56 mm | 1006.412598 px | 1007 | 1007 px |
| A4 / 33.04 mm | 872.768504 px | 873 | 873 px |
The repair uses `ceil(physicalHeight − 2 × effectiveMarginPx)`, matching these measured print fragmentainers. It is at most one CSS pixel (0.2646 mm) of platform quantization, not an arbitrary content buffer, a changed margin or a widened tolerance.
## Renderer and geometry contract
**Generated PDF remains authoritative.** `ThemedCvRenderer`, `PlaywrightCvPdfExporter`, their CSS/CLI flags, admission and submitted/frozen content are unchanged in this checkpoint.
`cvPreview.ts` replaces the former estimation function with native CSS column fragmentation. Each column has one page's physical width and usable height; selecting a page translates horizontally by that width inside the existing physical-page iframe. The application still presents the same vertically stacked page previews, zoom, scrolling and named keyboard region.
- Physical width/height come from the actual renderer's `.page` CSS. The existing selected-page height is only a fallback. A4 and Letter keep their existing dimensions.
- Effective top/bottom margin comes from the existing `--cv-page-margin` CSS token, including template/default margin and density. The duplicate frontend density/margin calculation is removed.
- Existing `@media print` declarations are reused in the screen fragmentainer. Header/main/sidebar padding, min-height, borders, fonts and content remain renderer-owned.
- Existing computed `avoid-page` break decisions are applied as `avoid-column` in the preview. Automatic flow, line height, `widows`/`orphans`, list/paragraph/section spacing and native element splitting stay with the browser engine.
- Print rules supply no separate CV footer/header space. No browser chrome, PDF margin, PDF pagination, font or document setting changes.
- The hidden measurement iframe loses its default border, so its content viewport agrees with visible borderless document frames. This is a measurement correction, not editor layout work.
The original failures had matching horizontal anchors and font sizes (within approximately 0.006 pt) while vertical/page membership diverged. The retained CSS confirms the same family/fallback chain, weight, line-height, letter spacing, paragraph/list gaps, entry/section gaps, box sizing and template borders. There is no font-specific shrink, substituted document font, generic safety padding or forced one-page fit.
## Measurement lifecycle and fonts
Each document waits for `document.fonts.ready` and image decode, applies the existing print decisions, then waits for two animation frames to settle layout. It publishes a single `cv-preview-ready` event and page/geometry metadata. The parent consumes that settled measurement, rejects an obsolete frame/document callback and does not count the previous continuous scroll height. No arbitrary sleep or continuous resize/mutation re-pagination loop is added.
The fidelity fixture waits for this explicit readiness signal, including standalone document captures. Horizontal columns are normalized into the **same physical-page coordinate system** used by the unchanged PDF oracle: column index from x; local x within that page; local y plus page index × physical height. Raw x/y and page membership are retained. This representation change does not change tolerance, expected marker membership or the PDF data.
Font families/fallbacks remain those documented in the original readiness record. Local preview/CLI use the same Chromium family; native PDFs retain their embedded selected/substituted font subsets. No font files are added, exposed or redistributed. Production Debian/font substitution and other browser engines still require deployment qualification; local evidence is not production font certification. New layout readiness waits do not claim to supply missing fonts.
Final evidence integrity checks confirm **16/16 server HTML inputs are byte-identical to the original readiness corpus**, **16/16 PDFs have identical extracted page text**, and all PDFs retain the same normalized embedded font names and page counts. PDF file-byte identity is not asserted because generated metadata can vary. All retained screenshot manifest hashes resolve correctly; no authentication state or raw browser traces are committed.
## Fidelity acceptance and evidence
The original numerical oracle is unchanged: page size ±0.5 mm; exact page count/marker membership; anchor position ±2 mm; font size ±0.5 pt; extractable RGB component ±1/255; repeated vertical safe area ±1 mm; no blank pages, missing/extra markers or hidden-interest leakage. All original 16 fixtures remain. Full wrapped-line/photo/all-font/all-template-long-form qualification is not implied by a numerical pass.
Both the repaired real-editor/native-export focused run and the subsequent complete browser run measured **16/16 passing**, exit 0, at the original tolerances. The final [numerical results](evidence/fidelity/fidelity-results.json), [source measurements](evidence/fidelity/measurements.json) and synthetic source HTML/native PDFs/page comparisons are retained under `evidence/fidelity/`.
| Fixture | Before preview pages | PDF pages | After preview pages | Measured result |
|---|---:|---:|---:|---|
| default-modern | 1 | 1 | 1 | Pass |
| default-minimal | 1 | 1 | 1 | Pass |
| default-executive | 1 | 1 | 1 | Pass |
| default-technical | 1 | 1 | 1 | Pass |
| default-ats-classic | 1 | 1 | 1 | Pass |
| default-nordic | 1 | 1 | 1 | Pass |
| default-elegant | 1 | 1 | 1 | Pass |
| default-creative | 1 | 1 | 1 | Pass |
| default-code | 1 | 1 | 1 | Pass |
| long-en-minimal | 3 | 4 | 4 | Pass |
| long-nb-code | 3 | 4 | 4 | Pass |
| compact-8mm-letter | 3 | 4 | 4 | Pass |
| roomy-28mm-nb | 4 | 5 | 5 | Pass |
| grid-and-bubbles | 1 | 1 | 1 | Pass |
| long-sidebar-nb | 4 | 5 | 5 | Pass |
| unbreakable-and-boundary | 4 | 4 | 4 | Pass, including corrected marker placement |
Total: **31 → 36 preview pages**, matching the unchanged **36 PDF pages**. Final-run maxima: x **0.0000037 mm**, y **1.493055 mm**, font **0.005026 pt**, extractable RGB component **0.0000451**. No count, marker membership, hidden-content, blank-page or vertical-safe-area failure is waived. These values do not claim pixel identity or exhaustive complete wrapped-line qualification.
Rendered comparisons show corrected continuation/bullet/section placement in all six former failures, including the compact Letter Languages section on page 4. Native short-sidebar surface height now agrees with print instead of being stretched to the screen page height. Small rasterization/glyph-box differences remain within the declared contract.
The PDF's existing long-entry keep rules can leave an entry heading plus company at the bottom of a page with bullets continuing next page (visible in the roomy fixture). Preview now represents that faithfully. Improving that native print-quality policy would change output semantics and is deliberately deferred; it is not hidden by a preview-only workaround.
## Preservation and changed owners
| PRES IDs | Invariant | Evidence |
|---|---|---|
| C06, E08/E09 | Same editor/preview panes, zoom/scroll, accessible name, focus entry/exit and responsive visibility | Inherited four P4-03 EN/NB × light/dark keyboard cases; rendered editor evidence |
| C07/C08/C09 | Override/source, hidden/order, template/settings and physical document geometry | All 16 unchanged source/settings fixtures; exact marker/content/page comparisons |
| C10, C01/C02 | Revision protection, canonical source separation, undo/redo | Existing 941 backend / editor conflict/history cases and native stale-write proof; owners untouched |
| C12 | Native PDF truth/content and admission | Unchanged native export endpoint/renderer; existing PDF and security suites |
| W05/W07, E05 | Selected versus frozen submitted CV and account ownership | Ordinary P2-D01/native package proof and account-isolation cases retained |
| C11 | Grounded AI review/accept/reject | Existing regressions retained; AI owners untouched |
Production files: new small `src/cvPreview.ts` isolates the existing document-preview owner for native behavioural testing; `CvBuilderEditor.tsx` consumes it and its settled measurements. No editor-frame/navigation/control redesign. Test files: `e2e/cv-pagination.spec.ts` adds two behaviours (six original failure memberships; delayed-image readiness), and `e2e/cv-readiness.spec.ts` records the new coordinate representation/readiness/performance without weakening the existing oracle.
## Performance
An offline benchmark uses the same saved HTML and Chromium, three runs of every fixture per implementation: **48 samples each**. Document setup through settled font/layout frames: median **48.75 → 52.28 ms**, p95 **59.19 → 62.84 ms**. This small local change is not production latency or a universal performance guarantee; scheduling/frame waits are included.
The [benchmark measurements](evidence/performance.json) preserve each sample. The [native ruler results](evidence/native-ruler/results.json) retain the independent page-capacity observation, with its three synthetic HTML inputs and generated PDFs. These are local probes, not new runtime dependencies or CI services.
The existing 300 ms preview debounce/API boundary remains. Pagination runs once per newly rendered document. Break-style reads are collected before writes to avoid an unnecessary alternating read/write loop. The existing architecture still loads the document into a measurement frame and each visible page frame; corrected page counts naturally add one frame to five formerly undercounted fixtures. Long-document DOM duplication remains an existing scaling limitation, not a newly introduced PDF request on each edit. No premature caching/virtualization platform is introduced.
## Visual and accessibility review
All **36** final comparison pages were reviewed as six contact-sheet overviews; selected template, continuation, final-page and boundary comparisons were also inspected at full resolution. This review finds matching document sequence, continued text, margins and sidebar fragment surfaces, including Languages on compact Letter page 4 and the corrected custom section on boundary page 4. The [visual manifest](evidence/visual-manifest.json) records hashes and review method; unreviewed standalone raster files are not labelled as individually inspected. Source HTML/native PDF/preview/PDF raster/comparison pairs remain available for every fixture.
The existing keyboard suite exercises EN/Bokmål × light/dark, named editor/preview regions, preview entry/exit, focus retention, zoom/scroll and phone pane visibility. It does not certify human screen-reader speech. Mounted-page visual and axe results from the final run are recorded below; incomplete findings remain incomplete. No editor/preview controls, layout, routes, reduced-motion rules or focus owners are redesigned.
All twelve retained mounted-editor screenshots were individually reviewed: one-page, multipage end and 390 px phone preview, each in EN/Bokmål × light/dark. Examples: [light/English desktop](evidence/one-page-light-en.png), [dark/Bokmål desktop](evidence/multipage-end-dark-nb.png), [light/Bokmål phone](evidence/preview-390-light-nb.png), [dark/English phone](evidence/preview-390-dark-en.png). Preview focus remains visible and document scrolling/page gaps remain usable. The deliberately unchanged fit-to-width phone rendering can have small document text; interaction/presentation improvements belong to later CV slices.
The [sixteen scoped axe scans](evidence/accessibility-summary.json) have **zero violations**, but retain **32 `aria-prohibited-attr` incomplete nodes and 252 `color-contrast` incomplete nodes**, each rule appearing in all 16 scans. Those are not passes or human acceptance. Earlier scans had fewer incomplete contrast nodes; the retained multipage DOM/visibility changes alter scanned node population, not proof of contrast compliance. Template-card descriptions and their localisation/disabled appearance remain existing controls debt, not part of this repair.
## Final validation and decision
Executed against the final code/test candidate on 2026-10-07. Commands use the repository-required `rtk` prefix (`rtk proxy` for unfiltered runners); below, prefixes are omitted for readability. Root commands run in the checkout, `npm.cmd` commands in `job-tracker-ui`. Browser runs set `DOTNET_ROOT=C:/Users/Connor/.dotnet` and `DOTNET_HOST_PATH=C:/Users/Connor/.dotnet/dotnet.exe`. Complete browser acquisition uses the production export with `PLAYWRIGHT_STATIC_EXPORT=true`, `PLAYWRIGHT_PREBUILT_EXPORT=true`, `CV_FIDELITY_OUTPUT=../.artifacts/p5-09-pagination/final`, and `PLAYWRIGHT_JSON_OUTPUT_NAME=../.artifacts/p5-09-pagination/final-browser.json`. Specialized suites run sequentially because they share the disposable API port.
| Exact command / check | Final result |
|---|---|
| `C:/Users/Connor/.dotnet/dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj -c Release --no-restore --logger 'console;verbosity=minimal'` | **941/941 passed**, zero failures/skips; unchanged count. Includes revision/atomic writes, source/override, frozen package/ownership, import/CSV/PDF/security regressions |
| `npm.cmd test -- --runInBand --silent --json --outputFile=../.artifacts/p5-09-pagination-jest.json` | **422/422 passed**, 80 suites; unchanged count. Editor history/conflict/undo/recovery owners retained |
| `npm.cmd run test:e2e -- --output=../.artifacts/p5-09-pagination/final-browser --reporter=line,json` | **98/98 actual passes**, 15.9 minutes, zero failures/skips/flaky/expected-failure cases; [safe case summary](evidence/browser-summary.json) |
| `npm.cmd run typecheck` / `npm.cmd run lint` | Both passed; no suppression or CI/dependency change |
| `npm.cmd exec tsc -- --project ../.artifacts/p5-09-readiness-e2e-tsconfig.json --noEmit --incremental false` | All permanent browser fixtures type-check; ignored local config extends existing project types |
| `npm.cmd run build` with `NEXT_PUBLIC_API_BASE_URL=http://localhost:5302/api` | Frontend production static export passed |
| `C:/Users/Connor/.dotnet/dotnet.exe build JobTrackerApi/JobTrackerApi.csproj -c Release --no-restore -warnaserror` | API Release passed, zero warnings/errors |
| `npm.cmd run test:rate-limits -- --reporter=line` | **9/9 passed**, trusted principal/IP/session and forwarding-spoof boundaries retained |
| `npm.cmd exec playwright test -- --config=playwright.ingress.config.ts --output=../.artifacts/p5-09-pagination/ingress --reporter=line` | **6/6 passed**; local application behavior, not deployed production proof |
| `npm.cmd run test:pdf-admission -- --output=../.artifacts/p5-09-pagination/pdf-admission --reporter=line` | **1/1 passed**, actual cross-route capacity/recovery |
| `python scripts/test-ingress.py` | **3/3 passed**, configuration/Compose checks |
| `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 fixtures, no provider certification |
| Bundled Python `-X utf8 scripts/verify-cv-fidelity.py .artifacts/p5-09-pagination/final` | **16/16 pass, exit 0**; unchanged oracle/tolerances; all 36 pages retained |
| `python scripts/supply-chain.py scan-secrets` | Passed against the staged/tracked candidate; no high-confidence credential patterns |
| `git diff --check` / `git diff --cached --check` | Passed |
| `python .artifacts/p4-04-check-doc-links.py` | 145 current/root Markdown documents, zero missing local targets; historical archive excluded |
The bundled PDF Python executable is `C:/Users/Connor/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/python.exe`; it uses already available PDF extraction/rasterization libraries. No application dependency is added.
**Count reconciliation:** 941 backend and 422 frontend are unchanged. Browser **96 → 98** adds only the two permanent `cv-pagination.spec.ts` behaviours: original six failing fixtures' exact native membership, and image-gated one-time settled measurement. The existing corpus case still contains all 16 numerical fixtures. Four inherited P4-03 preview keyboard cases, native stale-write recovery and frozen-package/P2-D01 cases pass normally. P2-D01 has no expected-failure annotation.
The initial focused run had 8/9 passing: the corpus readiness poll dereferenced `documentElement` during the iframe's initial document transition. The final fixture uses a null-safe poll for the explicit readiness signal; the native three-case rerun passed, followed by the complete 98/98 run above. No test expectation, fixture, oracle threshold or difficult content was removed.
**P5-09: GO.** G-CV-REV remains accepted and regression-green. G-CV-RENDER is satisfied by the retained corpus: exact page counts/membership, no missing content/blank-page/hidden-section/safe-area failures, positional/typographic/color results within the original tolerances, and matching reviewed page boundaries. P4-03 keyboard behavior remains green. This is a local measured gate, not certification of every possible CV, browser, font or deployment environment.
Remaining qualifications: production Linux/font substitution, other browser engines, complete wrapped-line/photo/all-font/all-template-long-form comparisons, existing native heading/bullet keep policy and later template/control presentation. The synthetic delayed-image test proves measurement readiness, not photo/PDF equivalence. These are explicit limits of the accepted fixture contract; no remaining fixture has a material count/placement/clipping mismatch. Further qualification must retain PDF truth rather than weaken this oracle.
F04, P5-02 human screen-reader acceptance, incomplete accessibility/manual checks, Dashboard preference-save 500, Board scaling/native touch and deferred `followMode` UI remain separate. No unrelated debt is closed by this repair. No backend/schema/export/package/CI/provider changes occurred. Stop before P5-09 and B07; the next authorised slice would be P5-09 editor frame/navigation using these unchanged output/revision contracts.