diff --git a/docs/ui-modernisation/CV_LIBRARY_LIFETIME.md b/docs/ui-modernisation/CV_LIBRARY_LIFETIME.md new file mode 100644 index 00000000..4a28f3c3 --- /dev/null +++ b/docs/ui-modernisation/CV_LIBRARY_LIFETIME.md @@ -0,0 +1,22 @@ +# CV library action lifetime + +11 November 2026. Source commit `de2fb452`, based on `cef45404`, branch `codex/a3-ui-10`. + +The library allowed a create response to navigate after unmount and a PDF response to trigger a download after leaving or session invalidation. Confirmed delete and a rename read could also continue into a write after session loss. Duplicate completion could start another read and announce success in the stale context. + +Reused the existing session-boundary cookie/context/invalidation functions and the BackupCard mount-generation pattern. Guarded profile-outline completion, create navigation, duplicate reload/feedback, deletion confirmation/completion, rename prompt/read/write completion, PDF download and asynchronous error decoding. Existing action locking, focus, drafts, current-language feedback, revision payloads and confirmations remain. No JSX, route, schema, canonical profile, renderer or PDF style changes. + +## Verification + +- RED: seven new deferred-response cases failed on the original source; 24 other library cases were filtered out. The first wrapper had environment-assignment errors before Jest; Jest still executed and produced the seven expected assertion failures. Its raw receipt is retained, not presented as a clean command execution. +- GREEN: 85 distinct cases across five suites passed, no skips: CV library 35, CV API helpers 15, session boundary 15, job-export lifetime 16, backup lifetime four. This includes 11 added library cases. The earlier 31-case library pass overlaps this result. +- TypeScript `tsc --noEmit --incremental false`: exit 0. Scoped ESLint on the two changed files: exit 0. `git diff --check`: passed. +- Tests use real React/MUI dialogs with mocked API responses and controlled boundary signals. Existing session-boundary suite checks the actual boundary implementation separately. Active PDF test uses synthetic bytes and observes filename and URL revocation. + +## Limits and preservation + +No fresh export build/native browser journey, real API/session/cookie-switch integration, physical PDF or accessibility acceptance is claimed. The previous export is stale for this frontend change. No cancellation or rollback of an already accepted server write is promised; stale completions are discarded. Initial library reads remain owned by the existing useViewResource/API path, which is outside this fix. All final A3, security, Phase 10 and release gates remain open. AttachmentStorage destination-ownership race remains source-backed and unproven; it was not changed. + +Evidence is under [cv-library-lifetime](evidence/cv-library-lifetime/SOURCE_RECEIPTS.json). Normalised source hashes and raw receipt hashes bind this bounded result. + +AST graph refresh PASSED on an exact 1,003-file tracked-code snapshot on E: (E:/Storage/Projects/Active/temp docs/jobjakt-local-build/a3-cv-library-source). SOURCE_MANIFEST.json binds files to source commit de2fb452; 10,345 nodes and 36,111 edges. 51 no-symbol warnings retained. Code-only snapshot excludes documentation. Stale generated integration graph: all 2,721 files (97,063,540 bytes) preserved and SHA256-verified on E before replacing its C directory with a junction; no tracked source or evidence removed. diff --git a/docs/ui-modernisation/evidence/cv-library-lifetime/SOURCE_RECEIPTS.json b/docs/ui-modernisation/evidence/cv-library-lifetime/SOURCE_RECEIPTS.json new file mode 100644 index 00000000..8d749679 --- /dev/null +++ b/docs/ui-modernisation/evidence/cv-library-lifetime/SOURCE_RECEIPTS.json @@ -0,0 +1,19 @@ +{ + "sourceCommit": "de2fb452210b128a8b5a3729e5971b8e2ddbba7c", + "sourcesLfSha256": { + "job-tracker-ui/src/views/CvBuilderPage.tsx": "63eb7348efcf4aaf5f3cb7e5d98fe6c5223896ac7ad64caa4111e251135f162e", + "job-tracker-ui/src/cv-builder-page.test.tsx": "5871bfef73499f2c7b2d9424e245436ded8f2e0838c4d5e6c08b49525f9822b7", + "job-tracker-ui/src/sessionBoundary.ts": "09edca77c98294818d2ad0c409ce595f1b6ca0c1f60419fc1078b70f96f5b7b9", + "job-tracker-ui/src/cvBuilder.ts": "c021bafeabbc8ad3526d1b10b90ad8f7f0958ded8ed26eac8c1d28b53098b64d", + "job-tracker-ui/jest.config.cjs": "de323cd3aa8693b11a028b2c026682f05ab988d182ed860861a051d66e53c286" + }, + "evidenceSha256": { + "green.txt": "cdd95bb125faddbc952d3b3588b3329eb8100dda6c6322b04b10ae150393230f", + "lint.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "red.txt": "658bccf93a58b3ea639635ccc33810ef7b87163c45ab91425673d0b50afec9c1", + "results.json": "f9c9dccfd13445d19aa803467ebd3aa3410a2b10054b278b22dfa90709224c8a", + "SOURCE_RECEIPTS.json": "609aade9b200454fc4bf05cc7913615d2faba6718e2a64b74e1533f1311db183", + "types.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "graph.txt": "b2b80b86f868b619d66445bd6d71d750ca091f8d6bee5dc40758fade72f22a67" + } +} diff --git a/docs/ui-modernisation/evidence/cv-library-lifetime/graph.txt b/docs/ui-modernisation/evidence/cv-library-lifetime/graph.txt new file mode 100644 index 00000000..75c5bd08 --- /dev/null +++ b/docs/ui-modernisation/evidence/cv-library-lifetime/graph.txt @@ -0,0 +1,22 @@ +Re-extracting code files in . (no LLM needed)... + AST cache: 0/1004 files served from cache (0%); 1004 to extract + AST extraction: 100/1004 uncached files (9%) [16 workers] + AST extraction: 200/1004 uncached files (19%) [16 workers] + AST extraction: 300/1004 uncached files (29%) [16 workers] + AST extraction: 400/1004 uncached files (39%) [16 workers] + AST extraction: 500/1004 uncached files (49%) [16 workers] + AST extraction: 600/1004 uncached files (59%) [16 workers] + AST extraction: 700/1004 uncached files (69%) [16 workers] + AST extraction: 800/1004 uncached files (79%) [16 workers] + AST extraction: 900/1004 uncached files (89%) [16 workers] + AST extraction: 1000/1004 uncached files (99%) [16 workers] + AST extraction: 1004/1004 uncached files (100%) [16 workers] + warning: 8 .sql file(s) contributed nothing to the graph because a dependency is missing: tree_sitter_sql not installed. Install it with: pip install "graphifyy[sql]" (#1745) + warning: 51 code file(s) yielded no symbols (0.12 MB) — a data file, or source this extractor models no symbols for: JobTrackerApi.Tests/PdfBrowserStub/Program.cs, JobTrackerApi/Program.cs, JobTrackerApi/Properties/AssemblyInfo.cs, browser-extension/tests/capture-url.test.mjs, docs/cv-import/benchmarks/harness/Program.cs (+46 more) +Graph has 10345 nodes (above 5000 limit). Building aggregated community view... +graph.html written (aggregated: 273 community nodes, 2404 cross-community edges) +Tip: run with --obsidian for full node-level detail. +[graphify watch] Rebuilt: 10345 nodes, 36111 edges, 273 communities +[graphify watch] graph.json, graph.html and GRAPH_REPORT.md updated in graphify-out +Code graph updated. For doc/paper/image changes run /graphify --update in your AI assistant. +Tip: set GEMINI_API_KEY or GOOGLE_API_KEY to use Gemini for semantic extraction. diff --git a/docs/ui-modernisation/evidence/cv-library-lifetime/green.txt b/docs/ui-modernisation/evidence/cv-library-lifetime/green.txt new file mode 100644 index 00000000..bac61c8e --- /dev/null +++ b/docs/ui-modernisation/evidence/cv-library-lifetime/green.txt @@ -0,0 +1,7 @@ + +Test Suites: 5 passed, 5 total +Tests: 85 passed, 85 total +Snapshots: 0 total +Time: 25.539 s +Ran all test suites matching src/cv-builder-page.test.tsx|src/cvBuilder.test.ts|src/session-boundary.test.ts|src/backup-lifetime.test.tsx|src/job-export-lifetime.test.tsx. +Test results written to: E:\Storage\Projects\Active\temp docs\jobjakt-local-build\cv-library-results.json diff --git a/docs/ui-modernisation/evidence/cv-library-lifetime/lint.txt b/docs/ui-modernisation/evidence/cv-library-lifetime/lint.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/ui-modernisation/evidence/cv-library-lifetime/red.txt b/docs/ui-modernisation/evidence/cv-library-lifetime/red.txt new file mode 100644 index 00000000..9c180fb5 Binary files /dev/null and b/docs/ui-modernisation/evidence/cv-library-lifetime/red.txt differ diff --git a/docs/ui-modernisation/evidence/cv-library-lifetime/results.json b/docs/ui-modernisation/evidence/cv-library-lifetime/results.json new file mode 100644 index 00000000..7ae3aef3 --- /dev/null +++ b/docs/ui-modernisation/evidence/cv-library-lifetime/results.json @@ -0,0 +1 @@ +{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":5,"numPassedTests":85,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":5,"numTotalTests":85,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1794419341420,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":266,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en library identifies templates by their human label and retains an unknown legacy identifier","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1794419348889,"status":"passed","title":"en library identifies templates by their human label and retains an unknown legacy identifier"},{"ancestorTitles":[],"duration":102,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb library identifies templates by their human label and retains an unknown legacy identifier","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1794419349156,"status":"passed","title":"nb library identifies templates by their human label and retains an unknown legacy identifier"},{"ancestorTitles":[],"duration":360,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"late CV creation cannot navigate after unmount","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419349258,"status":"passed","title":"late CV creation cannot navigate after unmount"},{"ancestorTitles":[],"duration":236,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"late CV creation cannot navigate after session","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419349618,"status":"passed","title":"late CV creation cannot navigate after session"},{"ancestorTitles":[],"duration":214,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"late CV PDF cannot download after unmount","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419349854,"status":"passed","title":"late CV PDF cannot download after unmount"},{"ancestorTitles":[],"duration":158,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"late CV PDF cannot download after session","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419350068,"status":"passed","title":"late CV PDF cannot download after session"},{"ancestorTitles":[],"duration":248,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"session invalidation while confirming deletion prevents the write","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419350226,"status":"passed","title":"session invalidation while confirming deletion prevents the write"},{"ancestorTitles":[],"duration":315,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"session invalidation during the rename read prevents a later revisioned write","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419350474,"status":"passed","title":"session invalidation during the rename read prevents a later revisioned write"},{"ancestorTitles":[],"duration":166,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a duplicate completing in an invalid session neither reloads nor announces success","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419350789,"status":"passed","title":"a duplicate completing in an invalid session neither reloads nor announces success"},{"ancestorTitles":[],"duration":184,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"an active CV PDF download retains its filename and releases the synthetic URL","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1794419350956,"status":"passed","title":"an active CV PDF download retains its filename and releases the synthetic URL"},{"ancestorTitles":[],"duration":268,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"session invalidation while naming a CV prevents the rename read and write","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419351141,"status":"passed","title":"session invalidation while naming a CV prevents the rename read and write"},{"ancestorTitles":[],"duration":154,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"an export error decoded after session invalidation cannot show delayed feedback","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419351409,"status":"passed","title":"an export error decoded after session invalidation cannot show delayed feedback"},{"ancestorTitles":[],"duration":283,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a profile outline completing after session invalidation cannot populate the create draft","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419351564,"status":"passed","title":"a profile outline completing after session invalidation cannot populate the create draft"},{"ancestorTitles":[],"duration":367,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"blank creation explicitly excludes all profile sections and contact even after template selection","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419351847,"status":"passed","title":"blank creation explicitly excludes all profile sections and contact even after template selection"},{"ancestorTitles":[],"duration":638,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"profile creation includes only deliberately selected available sections and contact","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419352214,"status":"passed","title":"profile creation includes only deliberately selected available sections and contact"},{"ancestorTitles":[],"duration":572,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"unavailable profile sections block profile creation and allow an explicit blank fallback","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1794419352853,"status":"passed","title":"unavailable profile sections block profile creation and allow an explicit blank fallback"},{"ancestorTitles":[],"duration":2,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"export validation decodes structured and blob errors without relabelling unknown failures","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419353425,"status":"passed","title":"export validation decodes structured and blob errors without relabelling unknown failures"},{"ancestorTitles":[],"duration":64,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"the CV library has a section heading and announces its held initial read without showing an empty result","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1794419353427,"status":"passed","title":"the CV library has a section heading and announces its held initial read without showing an empty result"},{"ancestorTitles":[],"duration":149,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a failed CV list read has an explicit retry that clears the stale failure after recovery","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1794419353491,"status":"passed","title":"a failed CV list read has an explicit retry that clears the stale failure after recovery"},{"ancestorTitles":[],"duration":401,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a live language change retains an open CV name and template draft without refetching library data","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1794419353640,"status":"passed","title":"a live language change retains an open CV name and template draft without refetching library data"},{"ancestorTitles":[],"duration":676,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"held CV creation locks the draft and gallery, then keeps a failed draft for an explicit retry","invocations":1,"location":null,"numPassingAsserts":17,"retryReasons":[],"startAt":1794419354041,"status":"passed","title":"held CV creation locks the draft and gallery, then keeps a failed draft for an explicit retry"},{"ancestorTitles":[],"duration":278,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a locked CV template remains identifiable and cannot change the creation request","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419354718,"status":"passed","title":"a locked CV template remains identifiable and cannot change the creation request"},{"ancestorTitles":[],"duration":297,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a held duplicate blocks another action for the same CV and shows the persisted refreshed copy","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1794419354996,"status":"passed","title":"a held duplicate blocks another action for the same CV and shows the persisted refreshed copy"},{"ancestorTitles":[],"duration":544,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"rename reads the current revision and preserves its settings when saving the new name","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1794419355293,"status":"passed","title":"rename reads the current revision and preserves its settings when saving the new name"},{"ancestorTitles":[],"duration":520,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a rename conflict keeps the original library entry and never retries the write automatically","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1794419355837,"status":"passed","title":"a rename conflict keeps the original library entry and never retries the write automatically"},{"ancestorTitles":[],"duration":572,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"cancelling deletion returns focus to the surviving CV action control without removing the CV","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419356357,"status":"passed","title":"cancelling deletion returns focus to the surviving CV action control without removing the CV"},{"ancestorTitles":[],"duration":972,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a failed creation keeps its draft and translates its failure after closing and reopening in another language","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1794419356929,"status":"passed","title":"a failed creation keeps its draft and translates its failure after closing and reopening in another language"},{"ancestorTitles":[],"duration":452,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"generic PDF failure follows the current language both after failure and when a held request fails","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1794419357901,"status":"passed","title":"generic PDF failure follows the current language both after failure and when a held request fails"},{"ancestorTitles":[],"duration":529,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a held deletion preserves focus moved to another surviving control before the response arrives","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1794419358353,"status":"passed","title":"a held deletion preserves focus moved to another surviving control before the response arrives"},{"ancestorTitles":[],"duration":201,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"PDF status 503 shows an actionable message without retrying","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419358883,"status":"passed","title":"PDF status 503 shows an actionable message without retrying"},{"ancestorTitles":[],"duration":185,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"PDF status 504 shows an actionable message without retrying","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419359084,"status":"passed","title":"PDF status 504 shows an actionable message without retrying"},{"ancestorTitles":[],"duration":49,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"lists existing CVs from the variants API","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1794419359269,"status":"passed","title":"lists existing CVs from the variants API"},{"ancestorTitles":[],"duration":278,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"shows the empty state and creates a CV then navigates to the editor","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419359318,"status":"passed","title":"shows the empty state and creates a CV then navigates to the editor"},{"ancestorTitles":[],"duration":129,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV dashboard and create flow render in Bokmål","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419359596,"status":"passed","title":"CV dashboard and create flow render in Bokmål"},{"ancestorTitles":[],"duration":679,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV deletion requires confirmation and supports cancellation","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1794419359725,"status":"passed","title":"CV deletion requires confirmation and supports cancellation"}],"endTime":1794419360406,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\cv-builder-page.test.tsx","startTime":1794419343179,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":117,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"json completes and downloads only for the initiating session","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419362181,"status":"passed","title":"json completes and downloads only for the initiating session"},{"ancestorTitles":[],"duration":52,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"csv completes and downloads only for the initiating session","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419362299,"status":"passed","title":"csv completes and downloads only for the initiating session"},{"ancestorTitles":[],"duration":39,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"convert completes and downloads only for the initiating session","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419362352,"status":"passed","title":"convert completes and downloads only for the initiating session"},{"ancestorTitles":["json lifetime"],"duration":29,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"json lifetime a pending completion after unmount cannot download or announce success","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419362391,"status":"passed","title":"a pending completion after unmount cannot download or announce success"},{"ancestorTitles":["json lifetime"],"duration":28,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"json lifetime a pending completion after context cannot download or announce success","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419362420,"status":"passed","title":"a pending completion after context cannot download or announce success"},{"ancestorTitles":["json lifetime"],"duration":31,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"json lifetime a pending completion after event cannot download or announce success","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419362448,"status":"passed","title":"a pending completion after event cannot download or announce success"},{"ancestorTitles":["json lifetime"],"duration":41,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"json lifetime a stale rejection cannot display feedback in the new session","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419362480,"status":"passed","title":"a stale rejection cannot display feedback in the new session"},{"ancestorTitles":["convert lifetime"],"duration":17,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"convert lifetime a pending completion after unmount cannot download or announce success","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419362522,"status":"passed","title":"a pending completion after unmount cannot download or announce success"},{"ancestorTitles":["convert lifetime"],"duration":18,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"convert lifetime a pending completion after context cannot download or announce success","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419362539,"status":"passed","title":"a pending completion after context cannot download or announce success"},{"ancestorTitles":["convert lifetime"],"duration":14,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"convert lifetime a pending completion after event cannot download or announce success","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419362557,"status":"passed","title":"a pending completion after event cannot download or announce success"},{"ancestorTitles":["convert lifetime"],"duration":16,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"convert lifetime a stale rejection cannot display feedback in the new session","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419362571,"status":"passed","title":"a stale rejection cannot display feedback in the new session"},{"ancestorTitles":[],"duration":25,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"an already invalidated session starts neither an export nor a file read","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419362588,"status":"passed","title":"an already invalidated session starts neither an export nor a file read"},{"ancestorTitles":[],"duration":67,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"json delayed rejection uses the current UI language","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419362613,"status":"passed","title":"json delayed rejection uses the current UI language"},{"ancestorTitles":[],"duration":46,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"convert delayed rejection uses the current UI language","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419362680,"status":"passed","title":"convert delayed rejection uses the current UI language"},{"ancestorTitles":[],"duration":75,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en conversion distinguishes zero, one and multiple rows","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1794419362726,"status":"passed","title":"en conversion distinguishes zero, one and multiple rows"},{"ancestorTitles":[],"duration":77,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb conversion distinguishes zero, one and multiple rows","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1794419362802,"status":"passed","title":"nb conversion distinguishes zero, one and multiple rows"}],"endTime":1794419362880,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\job-export-lifetime.test.tsx","startTime":1794419360428,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":65,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"writes carry the page's server-issued context and preserve the same account","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419363507,"status":"passed","title":"writes carry the page's server-issued context and preserve the same account"},{"ancestorTitles":[],"duration":28,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"cookie switch rejects a stale callback before transport and never adopts B's context","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1794419363572,"status":"passed","title":"cookie switch rejects a stale callback before transport and never adopts B's context"},{"ancestorTitles":[],"duration":6,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"cross-tab storage event authUserKey invalidates and aborts outstanding requests","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419363600,"status":"passed","title":"cross-tab storage event authUserKey invalidates and aborts outstanding requests"},{"ancestorTitles":[],"duration":6,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"cross-tab storage event authUserKey invalidates and aborts outstanding requests","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419363606,"status":"passed","title":"cross-tab storage event authUserKey invalidates and aborts outstanding requests"},{"ancestorTitles":[],"duration":6,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"cross-tab storage event authSessionContext invalidates and aborts outstanding requests","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419363612,"status":"passed","title":"cross-tab storage event authSessionContext invalidates and aborts outstanding requests"},{"ancestorTitles":[],"duration":5,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"cross-tab storage event null invalidates and aborts outstanding requests","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419363619,"status":"passed","title":"cross-tab storage event null invalidates and aborts outstanding requests"},{"ancestorTitles":[],"duration":6,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"another session cannot be adopted even if the readable cookie signal is unchanged","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419363624,"status":"passed","title":"another session cannot be adopted even if the readable cookie signal is unchanged"},{"ancestorTitles":[],"duration":4,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"same-account reauthentication also discards the old page","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419363630,"status":"passed","title":"same-account reauthentication also discards the old page"},{"ancestorTitles":[],"duration":17,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a delayed private response cannot repopulate caches after invalidation","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419363634,"status":"passed","title":"a delayed private response cannot repopulate caches after invalidation"},{"ancestorTitles":[],"duration":6,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a late bootstrap read/error cannot overwrite or sign out a newly bound session (error=false)","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419363651,"status":"passed","title":"a late bootstrap read/error cannot overwrite or sign out a newly bound session (error=false)"},{"ancestorTitles":[],"duration":7,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a late bootstrap read/error cannot overwrite or sign out a newly bound session (error=true)","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419363657,"status":"passed","title":"a late bootstrap read/error cannot overwrite or sign out a newly bound session (error=true)"},{"ancestorTitles":[],"duration":8,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"server precondition failure clears the page without silently retrying a mutation","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1794419363664,"status":"passed","title":"server precondition failure clears the page without silently retrying a mutation"},{"ancestorTitles":[],"duration":5,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"BFCache restoration invalidates old private state","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419363672,"status":"passed","title":"BFCache restoration invalidates old private state"},{"ancestorTitles":[],"duration":8,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"anonymous public-page history does not require a new login","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419363677,"status":"passed","title":"anonymous public-page history does not require a new login"},{"ancestorTitles":[],"duration":7,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"cookie fallback still works without a cross-tab storage event","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419363685,"status":"passed","title":"cookie fallback still works without a cross-tab storage event"}],"endTime":1794419363692,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\session-boundary.test.ts","startTime":1794419362897,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":["moveItem"],"duration":2,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"moveItem moves an item forward","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419364341,"status":"passed","title":"moves an item forward"},{"ancestorTitles":["moveItem"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"moveItem moves an item backward","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419364343,"status":"passed","title":"moves an item backward"},{"ancestorTitles":["moveItem"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"moveItem no-op for equal or out-of-range indices","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1794419364344,"status":"passed","title":"no-op for equal or out-of-range indices"},{"ancestorTitles":["moveItem"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"moveItem does not mutate the input","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419364345,"status":"passed","title":"does not mutate the input"},{"ancestorTitles":["wrapSelection"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"wrapSelection wraps a selection and keeps the selection over the inner text","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419364346,"status":"passed","title":"wraps a selection and keeps the selection over the inner text"},{"ancestorTitles":["wrapSelection"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"wrapSelection inserts a placeholder when nothing is selected","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419364348,"status":"passed","title":"inserts a placeholder when nothing is selected"},{"ancestorTitles":["wrapSelection"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"wrapSelection wraps a link with a url suffix","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419364349,"status":"passed","title":"wraps a link with a url suffix"},{"ancestorTitles":["CV page measurement"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV page measurement uses the selected physical page size","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419364350,"status":"passed","title":"uses the selected physical page size"},{"ancestorTitles":["CV page measurement"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV page measurement rounds layout pixels without hiding partial pages","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1794419364351,"status":"passed","title":"rounds layout pixels without hiding partial pages"},{"ancestorTitles":["CV language compatibility"],"duration":0,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV language compatibility maps legacy Norwegian values to the supported Bokmål locale","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419364352,"status":"passed","title":"maps legacy Norwegian values to the supported Bokmål locale"},{"ancestorTitles":["CV language compatibility"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV language compatibility fits the physical page to the available preview width","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1794419364352,"status":"passed","title":"fits the physical page to the available preview width"},{"ancestorTitles":["CV template selection"],"duration":2,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV template selection new documents opt into revised design while legacy fixture defaults stay unchanged","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419364353,"status":"passed","title":"new documents opt into revised design while legacy fixture defaults stay unchanged"},{"ancestorTitles":["CV template selection"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV template selection explicit template Apply opts into revised design and preserves inclusion and authored content","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419364355,"status":"passed","title":"explicit template Apply opts into revised design and preserves inclusion and authored content"},{"ancestorTitles":["CV template selection"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV template selection restores the selected template visual defaults without discarding user content","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419364356,"status":"passed","title":"restores the selected template visual defaults without discarding user content"},{"ancestorTitles":["CV template selection"],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"CV template selection shows ATS templates first and keeps an active non-ATS template visible","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419364357,"status":"passed","title":"shows ATS templates first and keeps an active non-ATS template visible"}],"endTime":1794419364359,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\cvBuilder.test.ts","startTime":1794419363705,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":187,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 backup] unknown lifecycle never claims disabled and Retry only reads","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1794419366542,"status":"passed","title":"[P7-09 backup] unknown lifecycle never claims disabled and Retry only reads"},{"ancestorTitles":[],"duration":99,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 backup] synchronous duplicate and competing downloads admit only one request","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419366730,"status":"passed","title":"[P7-09 backup] synchronous duplicate and competing downloads admit only one request"},{"ancestorTitles":[],"duration":41,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 backup] disposal rejects a late readable export without downloading private bytes or notifying","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1794419366830,"status":"passed","title":"[P7-09 backup] disposal rejects a late readable export without downloading private bytes or notifying"},{"ancestorTitles":[],"duration":81,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 backup] disposal during the existing deletion prompt cannot issue a later delete","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1794419366872,"status":"passed","title":"[P7-09 backup] disposal during the existing deletion prompt cannot issue a later delete"}],"endTime":1794419366954,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\backup-lifetime.test.tsx","startTime":1794419364385,"status":"passed","summary":""}],"wasInterrupted":false} diff --git a/docs/ui-modernisation/evidence/cv-library-lifetime/types.txt b/docs/ui-modernisation/evidence/cv-library-lifetime/types.txt new file mode 100644 index 00000000..e69de29b