diff --git a/docs/redesign/phase-7/COMMITS.md b/docs/redesign/phase-7/COMMITS.md
index 4b2b44e..34cb99b 100644
--- a/docs/redesign/phase-7/COMMITS.md
+++ b/docs/redesign/phase-7/COMMITS.md
@@ -1,6 +1,6 @@
# Phase 7 — Commit and evidence ledger
-Every completed checkpoint or coherent repair is committed and pushed normally to `codex/ui-redesign-phase4`, with immediate direct remote=HEAD verification. Production main remains `27657f31131cd95d42f0e1290853ec9c9b3959a4`; no force push, main push or automatic deployment.
+Policy: commit each completed checkpoint or coherent repair and push normally to `codex/ui-redesign-phase4`, with direct remote=HEAD verification. The latest P7-07 interface is locally validated but publication is blocked by the configured Git host's connection failure; no current synchronization is claimed. Production main remains `27657f31131cd95d42f0e1290853ec9c9b3959a4`; no force push, main push or automatic deployment.
Entry is the completed Phase6 successor receipt `b5319981ad3d1bd635daff861453d23c0de5320b`. Its [historical ledger](../phase-6/COMMITS.md) retains P6-05 `4669fb42e604c4728e8c420846ba78f298a8d90c` and original runtime provenance. At P7-00 entry, application source was unchanged from P6-03 `bd47214bdcd74336788dca0f294b8ddb9403cac1`; inherited measured export `2LDblja_XnSBWSuY6ys_V`. Later migration rows record their own changed source/build identity.
@@ -92,3 +92,14 @@ Compose/send committed and normal push verified **223dcf57d26bab624261994b61a46f
[Contract report](p7-07/CONTRACT.md) and [portable results](p7-07/evidence/contract-results.json): final12/12 backend contracts (11 nativeSQLiteHTTP, one unexecutedMariaDBcommand/query composition),80/80 existing related/auth/draft cases on matchingReleaseDLL, actualProgram cookie-authenticated3/3 native and ordinaryP2-D01PASS. All26/33/23 captured sources remain unchanged across their runs. BackendRelease compile and held-native browser TypeScript pass; frontendproduct/export remains unchanged. Exact27 metadata fields, two bounded parameterized read queries, owner/parent/company scope, literalNorwegiansearch and knownUTC/local-date distinction preserve existing resources. All failed attempts, sharedWindows-SQLwhitespace repair and parameter-display fixture qualifications remain. No DocumentsUI/axe/manual/MariaDBruntime acceptance. New12backend and3browser derive1053/304; frontend739/94 unchanged, no integrated counts. Backendimplemented scope CONDITIONAL GO; final hygiene/normalpushpending. DocumentsUI follows verified contract push; all57groups56PARTIAL/C11DEFERRED and32routes, mainunchanged/productionNO-GO/noPhase8.
Final metadata-contract hygiene PASS observed 2026-10-14T16:00:00.842457+00:00: secret and working/staged/phase whitespace gates pass;18 docs/504 local links resolve,57 groups and32 original routes retained. Backend contract implemented CONDITIONAL GO; normal development push pending. Actual MariaDB execution and Documents UI remain unverified; main unchanged, production NO-GO.
+
+
+### P7-07 contract push verified; Documents UI entered
+
+Contract commit **8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be** was normally pushed and directly verified2026-10-14T16:02:14.215615UTC: clean HEAD = origin = live development; main27657f3 unchanged. The exact receipt is included by reference/hash in portable contract results. Fresh fetch2026-10-16 confirms no intervening commits. Documents UI entry records64/6 existing tests PASS and ordinary0/10 runtime RED against the null scaffold; implementation and final UI evidence remain pending. All57 groups stay56PARTIAL/C11DEFERRED; no manual, MariaDB-runtime or production acceptance follows.
+
+### P7-07 Documents interface — final local gate
+
+| Logical unit | Commit | Validation / push receipt |
+|---|---|---|
+| P7-07 Documents interface, final local gate | Commit identity recorded by the following actual publication receipt | [UI report](p7-07/UI.md), [portable results](p7-07/evidence/ui-results.json): final86/8 affected, new native8 + CV4 + ordinaryP2-D01, types/lint/build PASS on H6tIi63meKxiKJUAiZrOm. All18 images actually reviewed; axe15/0violations/101 retained incompletes. Original32routes + approvedDocuments route =33; all57 statuses unchanged. Implemented local CONDITIONAL GO; final hygiene PASS, actual commit/push follow. Recorded later fetches fail with port443 unreachable; cached refs are not direct verification. No production/Phase8 action |
diff --git a/docs/redesign/phase-7/DEBT.md b/docs/redesign/phase-7/DEBT.md
index 5a401bd..55244a5 100644
--- a/docs/redesign/phase-7/DEBT.md
+++ b/docs/redesign/phase-7/DEBT.md
@@ -1,5 +1,9 @@
# Phase 7 — Retained debt and mandatory gates
+P7-07's [final local UI evidence](p7-07/evidence/ui-results.json) adds no whole-group or production acceptance. All RD01–RD28 dispositions remain. Its 15 axe scans retain 101 incomplete nodes: 15 short notification badges, 85 overlapped form backgrounds and one partially obscured Dashboard label inside the navigation scroll container. These require the existing manual contrast/state qualifications; zero automated violations does not close them.
+
+Two bounded P7-11 residuals are recorded from actual images: a long English action wraps its final letter at 1024px, and the unchanged CV missing-source owner displays raw `Not Found` error text. The Documents attachment-error layout was corrected and reverified. The Git host became unreachable after verified contract push `8e4db12`; later fetch failures are recorded, normal UI push remains pending. Actual MariaDB, human/device/provider/output and production gates remain open.
+
This is the current entry disposition of every RD01–RD28 obligation in the [Phase5 debt register](../phase-5/p5-15/READINESS_DEBT.md), reconciled with [successor dispositions](../phase-5/p5-15/p5-15-04/DEBT_DISPOSITION.md), [Phase6 final report](../phase-6/p6-05/README.md) and [exact handoff](../phase-6/p6-05/PHASE7_HANDOFF.md). Historical records stay intact. This table updates scoped outcomes rather than copying obsolete source descriptions as current facts.
Production remains **NO-GO**, main unchanged. Independent bounded development can continue with explicit conditions; a demonstrated severe shared/security/data failure blocks its dependent propagation. Actual provider/production/destructive actions retain separate authority.
@@ -192,3 +196,8 @@ Compose/send committed and normal push verified **223dcf57d26bab624261994b61a46f
[Contract report](p7-07/CONTRACT.md) and [portable results](p7-07/evidence/contract-results.json): final12/12 backend contracts (11 nativeSQLiteHTTP, one unexecutedMariaDBcommand/query composition),80/80 existing related/auth/draft cases on matchingReleaseDLL, actualProgram cookie-authenticated3/3 native and ordinaryP2-D01PASS. All26/33/23 captured sources remain unchanged across their runs. BackendRelease compile and held-native browser TypeScript pass; frontendproduct/export remains unchanged. Exact27 metadata fields, two bounded parameterized read queries, owner/parent/company scope, literalNorwegiansearch and knownUTC/local-date distinction preserve existing resources. All failed attempts, sharedWindows-SQLwhitespace repair and parameter-display fixture qualifications remain. No DocumentsUI/axe/manual/MariaDBruntime acceptance. New12backend and3browser derive1053/304; frontend739/94 unchanged, no integrated counts. Backendimplemented scope CONDITIONAL GO; final hygiene/normalpushpending. DocumentsUI follows verified contract push; all57groups56PARTIAL/C11DEFERRED and32routes, mainunchanged/productionNO-GO/noPhase8.
Final metadata-contract hygiene PASS observed 2026-10-14T16:00:00.842457+00:00: secret and working/staged/phase whitespace gates pass;18 docs/504 local links resolve,57 groups and32 original routes retained. Backend contract implemented CONDITIONAL GO; normal development push pending. Actual MariaDB execution and Documents UI remain unverified; main unchanged, production NO-GO.
+
+
+### P7-07 contract push verified; Documents UI entered
+
+Contract commit **8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be** was normally pushed and directly verified2026-10-14T16:02:14.215615UTC: clean HEAD = origin = live development; main27657f3 unchanged. The exact receipt is included by reference/hash in portable contract results. Fresh fetch2026-10-16 confirms no intervening commits. Documents UI entry records64/6 existing tests PASS and ordinary0/10 runtime RED against the null scaffold; implementation and final UI evidence remain pending. All57 groups stay56PARTIAL/C11DEFERRED; no manual, MariaDB-runtime or production acceptance follows.
diff --git a/docs/redesign/phase-7/PRESERVATION.md b/docs/redesign/phase-7/PRESERVATION.md
index a55ba73..f5491a7 100644
--- a/docs/redesign/phase-7/PRESERVATION.md
+++ b/docs/redesign/phase-7/PRESERVATION.md
@@ -1,5 +1,7 @@
# Phase 7 — Initial preservation matrix
+Latest P7-07 UI reconciliation: [portable results](p7-07/evidence/ui-results.json) retain all 57 statuses below, with 56 PARTIAL and C11 DEFERRED. The original 32 routes remain alongside the approved `/documents` route (33 current patterns). The final affected 86 component cases and 13 native consumer/journey cases pass; ordinary P2-D01 is included. Human/device/output and whole-group qualifications remain. Publication is pending the configured Git host's recovery; cached remote refs are not live verification.
+
All **57 original protected groups** remain in scope. This initial matrix derives the exact IDs/statuses and original evidence/limitations from [Phase6 preservation](../phase-6/p6-05/PRESERVATION.md), reconciled against its [aggregate](../phase-6/p6-05/evidence/validation.json), [original functionality ledger](../audit/FUNCTIONALITY_INVENTORY.md), [Phase2 anchors](../phase-2/PRESERVATION_MATRIX.md) and [Phase3 impacts](../phase-3/PRESERVATION_IMPACT_MATRIX.md). The short rows never replace protected sub-capabilities.
Entry source is development receipt `b5319981ad3d1bd635daff861453d23c0de5320b`, with application frozen at `bd47214bdcd74336788dca0f294b8ddb9403cac1`. [Fresh P7-00 identity/route/matrix evidence](p7-00/evidence/baseline.json) adds finite static proof; runtime results remain the inherited accepted Phase6 run, not a new P7-00 execution.
@@ -202,3 +204,8 @@ Compose/send committed and normal push verified **223dcf57d26bab624261994b61a46f
[Contract report](p7-07/CONTRACT.md) and [portable results](p7-07/evidence/contract-results.json): final12/12 backend contracts (11 nativeSQLiteHTTP, one unexecutedMariaDBcommand/query composition),80/80 existing related/auth/draft cases on matchingReleaseDLL, actualProgram cookie-authenticated3/3 native and ordinaryP2-D01PASS. All26/33/23 captured sources remain unchanged across their runs. BackendRelease compile and held-native browser TypeScript pass; frontendproduct/export remains unchanged. Exact27 metadata fields, two bounded parameterized read queries, owner/parent/company scope, literalNorwegiansearch and knownUTC/local-date distinction preserve existing resources. All failed attempts, sharedWindows-SQLwhitespace repair and parameter-display fixture qualifications remain. No DocumentsUI/axe/manual/MariaDBruntime acceptance. New12backend and3browser derive1053/304; frontend739/94 unchanged, no integrated counts. Backendimplemented scope CONDITIONAL GO; final hygiene/normalpushpending. DocumentsUI follows verified contract push; all57groups56PARTIAL/C11DEFERRED and32routes, mainunchanged/productionNO-GO/noPhase8.
Final metadata-contract hygiene PASS observed 2026-10-14T16:00:00.842457+00:00: secret and working/staged/phase whitespace gates pass;18 docs/504 local links resolve,57 groups and32 original routes retained. Backend contract implemented CONDITIONAL GO; normal development push pending. Actual MariaDB execution and Documents UI remain unverified; main unchanged, production NO-GO.
+
+
+### P7-07 contract push verified; Documents UI entered
+
+Contract commit **8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be** was normally pushed and directly verified2026-10-14T16:02:14.215615UTC: clean HEAD = origin = live development; main27657f3 unchanged. The exact receipt is included by reference/hash in portable contract results. Fresh fetch2026-10-16 confirms no intervening commits. Documents UI entry records64/6 existing tests PASS and ordinary0/10 runtime RED against the null scaffold; implementation and final UI evidence remain pending. All57 groups stay56PARTIAL/C11DEFERRED; no manual, MariaDB-runtime or production acceptance follows.
diff --git a/docs/redesign/phase-7/README.md b/docs/redesign/phase-7/README.md
index 08dcbbf..48479c1 100644
--- a/docs/redesign/phase-7/README.md
+++ b/docs/redesign/phase-7/README.md
@@ -16,7 +16,7 @@ The entry commit is `b5319981ad3d1bd635daff861453d23c0de5320b` on `codex/ui-rede
| [P7-05](p7-05/README.md) | Discovery, Companies, Offers and Reminders | Entry bfa0ab6 verified; Discovery21/1,new8+existing4native/backend14/types/lint/build pass;18 actual PNG reviews/qualified81+52incompletes. Discovery implemented CONDITIONAL GO, hygiene PASS/normal push34a4d6d verified; Companies30/4,new6+old2+named4native/backend2/types/lint/build pass;20actual PNG reviews/qualified91+163incompletes. Companies implemented CONDITIONAL GO, hygiene PASS/normal pusha8aba85 verified; Offers9/1,new4native/backend6/types/lint/build pass;14actual PNG reviews/15incompletes qualified. Offers implemented CONDITIONAL GO, hygiene PASS/normal pushc723a4e verified; Reminders17/3,new6native/backend6/types/lint/build pass;17actual PNG reviews/114incompletes qualified. P7-05 implemented CONDITIONAL GO, hygiene PASS/normal push627955a directly verified11:49:32UTC, clean localHEAD=origin=live/main unchanged; P7-06 next |
| [P7-06](p7-06/README.md) | Remaining Application forms and global correspondence | All three boundaries implemented CONDITIONAL GO: forms4c325/inboxa791 normally pushed; [compose](p7-06/COMPOSE.md) Compose affected52/5 and fresh backend25 PASS; native10+inbox8+ordinaryP2-D01=19 PASS on LFCrTZzDtX5hNBSRBj5Xx; app/browser TypeScript, lint and build PASS. Actual image review 47/47; all-enabled44scans0violations/330incompletes triaged, human/device/provider qualifications remain. +10front/+0suites,+10browser,+7backend derive739/94,301,1041 only; no integrated full-suite claim. Hygiene PASS and normal223dcf5 push directly verified11:03:15UTC;06Fdeferred/57groups32routes/main unchanged; P7-07 contract first next |
| P7-06F | Deferred `followMode` reviewed composer | Optional separately reviewed mail slice; **DEFERRED** until mode→draft/review action and fallback are explicitly decided |
-| [P7-07](p7-07/README.md) | Documents owner-scoped index | Backendcontract implemented CONDITIONAL GO:12/12 new contracts,80/80 related/auth/draft,actualcookie native3+ordinaryP2-D01PASS;26/33/23sourcesheld. Finalhygiene/normalpushpending; actual MariaDB runtime NOT VERIFIED. UIwaitscontractpush; no app route added yet |
+| [P7-07](p7-07/README.md) | Documents owner-scoped index | Implemented local scope CONDITIONAL GO. Contract pushed `8e4db12`; [UI](p7-07/UI.md) final affected86/8, native8+CV4+ordinaryP2-D01, types/lint/build PASS. All18 final images reviewed; axe15 scans0 violations/101 retained incompletes. Original32 routes plus `/documents` =33. Hygiene PASS; publication blocked by the unreachable Git host, no live synchronization claim. Actual MariaDB/human/device/production qualifications remain |
| P7-08 | Existing notifications and operations | Existing store and real destinations; new enabled producers require F20 recovery evidence; planned |
| P7-09 | Remaining Settings, Connections and data lifecycle | Existing independent save scopes and sensitive-action guards; planned |
| P7-10 | Admin operational presentation | Existing distinct Users/Audit/System routes and server authorization; planned |
@@ -80,3 +80,8 @@ Compose/send committed and normal push verified **223dcf57d26bab624261994b61a46f
[Contract report](p7-07/CONTRACT.md) and [portable results](p7-07/evidence/contract-results.json): final12/12 backend contracts (11 nativeSQLiteHTTP, one unexecutedMariaDBcommand/query composition),80/80 existing related/auth/draft cases on matchingReleaseDLL, actualProgram cookie-authenticated3/3 native and ordinaryP2-D01PASS. All26/33/23 captured sources remain unchanged across their runs. BackendRelease compile and held-native browser TypeScript pass; frontendproduct/export remains unchanged. Exact27 metadata fields, two bounded parameterized read queries, owner/parent/company scope, literalNorwegiansearch and knownUTC/local-date distinction preserve existing resources. All failed attempts, sharedWindows-SQLwhitespace repair and parameter-display fixture qualifications remain. No DocumentsUI/axe/manual/MariaDBruntime acceptance. New12backend and3browser derive1053/304; frontend739/94 unchanged, no integrated counts. Backendimplemented scope CONDITIONAL GO; final hygiene/normalpushpending. DocumentsUI follows verified contract push; all57groups56PARTIAL/C11DEFERRED and32routes, mainunchanged/productionNO-GO/noPhase8.
Final metadata-contract hygiene PASS observed 2026-10-14T16:00:00.842457+00:00: secret and working/staged/phase whitespace gates pass;18 docs/504 local links resolve,57 groups and32 original routes retained. Backend contract implemented CONDITIONAL GO; normal development push pending. Actual MariaDB execution and Documents UI remain unverified; main unchanged, production NO-GO.
+
+
+## Contract push and UI continuation — 2026-10-16
+
+Metadata contract normal push directly verified as **8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be** at 2026-10-14T16:02:14.215615 UTC: clean HEAD = origin = live development; main27657f3 unchanged. Backend implemented scope remains CONDITIONAL GO, with actual MariaDB execution unverified. A fresh fetch on 2026-10-16 found no intervening commits. The separate Documents interface now proceeds; its entry and bounded requirements are recorded in the P7-07 UI report. Earlier pending-push statements are historical observations. No production deployment or Phase8 implementation.
diff --git a/docs/redesign/phase-7/p7-07/CONTRACT.md b/docs/redesign/phase-7/p7-07/CONTRACT.md
index b4c9fac..ba1de4b 100644
--- a/docs/redesign/phase-7/p7-07/CONTRACT.md
+++ b/docs/redesign/phase-7/p7-07/CONTRACT.md
@@ -1,6 +1,6 @@
# P7-07 — Documents metadata contract
-**Status: backend contract implemented, implemented-scope CONDITIONAL GO; final affected/native checks pass. Hygiene, commit and normal push remain pending. P7-07 remains ongoing; Documents UI waits for the contract push.** This is the first P7-07 boundary in the [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries), implementing the owner projection required by [Documents architecture](../../phase-3/DOCUMENTS_UX.md).
+**Status: backend contract implemented, implemented-scope CONDITIONAL GO; affected/native checks and hygiene pass. Normal push verified as8e4db12. P7-07 remains ongoing; the separate [Documents UI](UI.md) is in progress.** This is the first P7-07 boundary in the [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries), implementing the owner projection required by [Documents architecture](../../phase-3/DOCUMENTS_UX.md).
## Bounded implementation
@@ -83,3 +83,8 @@ All **57 PRES groups remain 56 PARTIAL / C11 DEFERRED**; C05–C12/W05–W08/M04
**Contract implemented-scope acceptance: CONDITIONAL GO; hygiene and normal push pending. P7-07: ongoing; Documents UI not started. Actual MariaDB execution and manual acceptance remain unverified. Production: NO-GO; main unchanged. Phase 8: no implementation.**
Final metadata-contract hygiene PASS observed 2026-10-14T16:00:00.842457+00:00: secret and working/staged/phase whitespace gates pass;18 docs/504 local links resolve,57 groups and32 original routes retained. Backend contract implemented CONDITIONAL GO; normal development push pending. Actual MariaDB execution and Documents UI remain unverified; main unchanged, production NO-GO.
+
+
+## Contract push and UI continuation — 2026-10-16
+
+Metadata contract normal push directly verified as **8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be** at 2026-10-14T16:02:14.215615 UTC: clean HEAD = origin = live development; main27657f3 unchanged. Backend implemented scope remains CONDITIONAL GO, with actual MariaDB execution unverified. A fresh fetch on 2026-10-16 found no intervening commits. The separate Documents interface now proceeds; its entry and bounded requirements are recorded in the P7-07 UI report. Earlier pending-push statements are historical observations. No production deployment or Phase8 implementation.
diff --git a/docs/redesign/phase-7/p7-07/README.md b/docs/redesign/phase-7/p7-07/README.md
index 45775ba..d2baab9 100644
--- a/docs/redesign/phase-7/p7-07/README.md
+++ b/docs/redesign/phase-7/p7-07/README.md
@@ -1,6 +1,6 @@
# P7-07 — Documents owner-scoped index
-**Backend implemented scope: CONDITIONAL GO; final hygiene and normal push pending. Documents UI has not started.** Follow the exact [Phase 6 handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries) and [Documents architecture](../../phase-3/DOCUMENTS_UX.md). Implement and push the metadata projection before the interface.
+**Implemented local scope: CONDITIONAL GO; final hygiene PASS.** The backend contract was normally pushed as `8e4db12`; the separate [Documents interface](UI.md) now passes its affected component, native, build and finite visual checks. The configured Git host is currently unreachable, so publication is pending and this checkpoint is not closed or reported synchronized. Follow the exact [Phase 6 handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries) and [Documents architecture](../../phase-3/DOCUMENTS_UX.md). The contract was pushed before interface implementation.
The preceding compose commit `223dcf57d26bab624261994b61a46f2d3e2dd5b2` was normally pushed and directly verified at **2026-10-14 11:03:15.576853 UTC**: clean HEAD = origin = live development; local/origin/live main remains `27657f31131cd95d42f0e1290853ec9c9b3959a4`. [Entry evidence](evidence/entry.json) records the receipt and fresh existing-contract baseline: **37 passing cases**, unchanged Release DLL, with all20 captured sources unchanged before/after. This is an existing-contract subset, not a Documents endpoint or full-suite execution.
@@ -22,10 +22,27 @@ Keep ordinary P2-D01 and exact frozen HTML/letter/archive-byte proof. The old Ph
All57 preservation groups remain56 PARTIAL / C11 DEFERRED, with32 original routes preserved. Derived inventory is739/94 frontend,301 browser,1041 backend; it is not a fresh integrated pass count. Human reader, physical touch/software keyboard, zoom, provider/output and release conditions remain open. P5-13 is withheld; P7-06F is deferred; main unchanged, production NO-GO. No Phase8 implementation.
-Next: finish the backend contract and its bounded evidence, commit/push/verify it, then migrate the Documents interface using the existing shared design system.
+The observations below retain the contract's chronology. The latest interface result is recorded at the end and in its separate report; earlier pending statements are historical.
## Final backend observation
[Contract report](CONTRACT.md) and [portable results](evidence/contract-results.json) record final12/12 new contracts,80/80 related/auth/draft cases and actualProgram/nativeSQLite cookie3/3 plus ordinaryP2-D01PASS. Full .NET whitespace semantics, ordinal Unicode marker handling, actual bound pagination parameters, frozen metadata/bytes and strict tenant cases retain ordinary assertions. All failed attempts and source/timing qualifications remain. Final26/33/23 sources and binaries match; Release compilation and browser types pass. There is no DocumentsUI/axe/physical/human acceptance yet, and actualMariaDB execution remains unverified. Derivedbackend1053/browser304/frontend739/94 are not integrated passing counts. Hygiene and normal development push follow; the UI starts only after verified contract push.
Final metadata-contract hygiene PASS observed 2026-10-14T16:00:00.842457+00:00: secret and working/staged/phase whitespace gates pass;18 docs/504 local links resolve,57 groups and32 original routes retained. Backend contract implemented CONDITIONAL GO; normal development push pending. Actual MariaDB execution and Documents UI remain unverified; main unchanged, production NO-GO.
+
+
+## Contract push and UI continuation — 2026-10-16
+
+Metadata contract normal push directly verified as **8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be** at 2026-10-14T16:02:14.215615 UTC: clean HEAD = origin = live development; main27657f3 unchanged. Backend implemented scope remains CONDITIONAL GO, with actual MariaDB execution unverified. A fresh fetch on 2026-10-16 found no intervening commits. The separate Documents interface now proceeds; its entry and bounded requirements are recorded in the P7-07 UI report. Earlier pending-push statements are historical observations. No production deployment or Phase8 implementation.
+
+## Documents interface — final local evidence
+
+[Portable UI results](evidence/ui-results.json) record **86 cases in eight frontend suites**, **eight new Documents browser journeys**, **four existing CV frame journeys** and **ordinary P2-D01**, all passing on the final captured source. App/browser TypeScript, lint and production build pass. The build is `H6tIi63meKxiKJUAiZrOm`; all 29 source existence/hash records and three API/test/build identities match before and after the final native batch. This interface changes no backend source or renderer.
+
+All **18 final images** were actually reviewed: 17 Documents states/layouts and the existing frozen-package consumer image. The 15 default-enabled plus target-size axe scans have zero violation nodes and **101 incomplete nodes**, all retained and contextually triaged. Human screen-reader, real zoom, physical touch/software keyboard and production output acceptance remain open. A cramped attachment-error action was moved below its message and verified in the rebuilt page. Minor long-action wrapping at 1024px and the existing raw CV `Not Found` error copy remain explicit P7-11 residuals.
+
+The original 32 routes remain; `/documents` adds a supported 33rd route. All 57 preservation groups remain 56 PARTIAL / C11 DEFERRED. Added coverage derives frontend **755/95**, browser **312**, backend **1,053**; these are inventories, not a fresh integrated regression result. P7-12 still owns that run. P5-13 remains withheld, followMode deferred and production NO-GO. `main` is unchanged; Phase 8 has not started.
+
+The configured Git host refused port-443 connections during the later recorded fetch attempts. Commit/push outcome will be recorded after the actual attempt. P7-08 through P7-12 remain; their approved order and dependencies are unchanged.
+
+Final [static checks](evidence/ui-static-checks.json) passed at **2026-10-16 16:21 UTC**: secret scan and working/staged/phase whitespace checks, 19 documents / 518 local links, all 57 preservation groups and 33 current routes. Static checks verify local main/origin-main refs only; they do not establish current remote or production state.
diff --git a/docs/redesign/phase-7/p7-07/UI.md b/docs/redesign/phase-7/p7-07/UI.md
new file mode 100644
index 0000000..39a3d1e
--- /dev/null
+++ b/docs/redesign/phase-7/p7-07/UI.md
@@ -0,0 +1,99 @@
+# P7-07 — Documents interface
+
+**Status: implemented-scope CONDITIONAL GO locally; latest affected frontend and native 13/13 pass, all 18 final images are reviewed and final hygiene passes. Commit and normal push follow.** This is the second boundary in the [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries), following the [metadata contract](CONTRACT.md). It reuses existing resource owners and the Phase 4–6 design system.
+
+## Entry and bounded scope
+
+The metadata contract was committed and normally pushed as `8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be`. Direct remote verification at **2026-10-14T16:02:14.215615 UTC** recorded clean HEAD = origin = live development. Local/origin/live main remained `27657f31131cd95d42f0e1290853ec9c9b3959a4`. Its exact receipt is retained at `.artifacts/phase7/p7-07/contract-push-receipt.json`; the portable contract results record that successor observation.
+
+On **2026-10-16**, a fresh fetch confirmed development HEAD/origin still `8e4db12`, with only the untracked null-return `DocumentsPage.tsx` scaffold. No execution or product changes are inferred during the pause. Existing focused frontend regression runs precede implementation. The authoritative last full frontend run remains 573/89; the derived inventory before this interface is frontend739/94, browser304, backend1053. These are inventories, not new full-suite passing counts.
+
+Later fetches at **15:52 and 15:55 UTC** fail because the configured remote host on port 443 is unreachable. The third fetch, **16:12:35.608743–16:12:56.824123**, exits **128** for the same connection failure; `remote-retry-20261016-1613/fetch-command.json` and `fetch.log` retain it. Fresh remote verification remains pending. The earlier entry receipt is not a claim of current remote identity; normal push and remote verification remain required.
+
+The authenticated `/documents` page sits under Career, with a new entry in the existing command palette and paired English/Bokmål labels. The approved index adds one route, bringing the inventory to **33 while preserving the original 32**. It uses the existing shell heading, semantic sections, table/mobile-card patterns, shared theme tokens and feedback infrastructure. No new renderer, editor, generic Document store, account upload bucket, mail persistence or dependency is introduced.
+
+Changed product files are `views/DocumentsPage.tsx`, `App.tsx`, `components/QuickCommandDialog.tsx` and `i18n/translations.ts`. Coverage adds `documents-page.test.tsx` and `e2e/phase7-documents.spec.ts`, plus one ordinary case in `quick-command-preservation.test.tsx`. The existing command-list length assertion increases from six to seven for the actual additional command. Backend contract/source behavior remains unchanged.
+
+The page reads bounded metadata through `/api/documents`, 25 rows per server page. Search, supported filters, sorting and pagination preserve route context, browser history, unknown query parameters, hash and router state. Search edits replace their history entry; filter/page actions retain deliberate history. Superseded requests and incompatible query results cannot display stale rows. Refresh and load Retry perform guarded reads only. Language changes reformat existing metadata without refetching it. Linked-job selection reuses the existing bounded owner-scoped choices endpoint with search, limit 20 and separate loading/error feedback.
+
+The page labels current/draft/frozen resources, independent CV public/private state, source versions and honest recorded-date meanings. Uploaded local dates retain their recorded wall clock; the timezone is unknown. English uses `en-GB`, Bokmål `nb-NO`; unknown dates remain unknown. Saved letter versions are distinguished from current text, including history after current text is cleared. Captured CV version/name stays frozen provenance. A captured package does not prove receipt; a draft does not prove delivery; a public CV does not imply submission. Uploaded certificates remain uploaded evidence, and the existing AI-use flag is read-only here.
+
+Open actions navigate to existing resource owners and their dirty-state guards, carrying the Documents return URL/state. Correspondence links provide context, not exact-ID Resume; Submission links do not promise a selected snapshot. Current CV export stays in its existing owner. Exact type/resource/source relationships guard actions; malformed external or mismatched paths are not offered. Percent-encoded historical Nordic public slugs remain usable without fetching their bodies.
+
+Attachment download is an explicit, single-flight authenticated API-relative blob GET, avoiding a doubled `/api` prefix. It retains response MIME and the displayed filename, disposes the object URL, reports failure and suppresses late downloads after unmount or query change. File Retry repeats that file GET; actual404 offers list Refresh. Completion restores its initiating button only while the user remains on the temporary results focus target, preserving deliberate Search focus. Listing does not generate PDFs, invoke AI, capture packages or fetch private content bodies.
+
+## Demonstrated findings and repairs
+
+| Demonstrated issue | Bounded correction |
+|---|---|
+| A valid server result with no rows on an out-of-range page was presented as an empty collection | Distinguish out-of-range page, genuinely empty index, filtered no-match and request failure. Show explicit first-page recovery while preserving filters/context; no silent page change |
+| Literal search `all` was discarded by generic default-value cleanup | Remove `all` only for the three enumerated filter defaults; preserve free-text search and unrelated URL/hash/state |
+| Selecting sort and immediately typing Search could rebuild the URL from stale committed parameters, losing `name-asc` | Keep pending edits in a small `{ location, params }` ref until Router commits. Compose consecutive edits; after a committed Back/Forward navigation use its authoritative parameters. Preserve unknown query/hash/router state without changing state frameworks or backend contracts |
+| Download completion could reclaim focus after the user moved to Search | Restore the originating action only from its temporary results focus target and matching query; preserve deliberate user focus |
+| Generic action-path validation could hide historical percent-encoded public CV slugs | Validate the expected owned resource/source and constrained public path while retaining valid encoded slugs |
+| Actual review of the 1440px attachment404 image finds the Alert action squeezing message text to roughly 70px | Give that existing Alert `auto minmax(0, 1fr)` grid columns, allow the message column to shrink and place its action on a separate second row. Styles only; existing refresh/retry behavior and tests stay unchanged |
+
+Neutral count labels handle one/many values without ungrammatical suffixes. Fields and state changes remain catalogue-backed. The page keeps white print documents in their existing owners; no pagination or rendering geometry is changed for this interface.
+
+## Executed finite evidence
+
+[Entry evidence](evidence/ui-entry.json) records commands and source receipts; [current UI results](evidence/ui-results.json) consolidate the final finite evidence with hygiene/publication status. All times below are UTC on **2026-10-16**. Raw reports and retained failures live under `.artifacts/phase7/current/unit/` and `.artifacts/phase7/p7-07/`; frozen pre/post inputs qualify the earlier observations separately from the final candidate.
+
+| Observation | Actual result and qualification |
+|---|---|
+| Existing consumers | **64/6 PASS**, 08:59:12.556650–08:59:53.028496; navigation/command/notifications/CV-library/attachment/application-assets suites, with captured inputs unchanged |
+| Initial scaffold RED | **0 pass/10 fail**, 09:00:57.645492–09:01:47.462948; ordinary missing rendered content/actions, zero pending/todo. Scaffold and Documents tests stay unchanged across RED; the separate command addition is not executed here |
+| First candidate | **82/8 PASS**, 09:12:21.430119–09:12:51.706771. Separate app TypeScript exits **2**, 09:12:21.432827–09:12:37.343677, for two new test-utility typing errors. Fixes remove unsupported role-option typing and narrow an unknown value without weakening assertions |
+| Pagination RED | **11 pass/1 fail**, 09:14:36.074153–09:14:46.099718; ordinary out-of-range recovery regression in `ui-page-red` |
+| Literal-search RED | **12 pass/1 fail**, 09:17:36.435668–09:17:44.801611; exact `all` search regression in `ui-literal-red` |
+| Combined focus RED | **12 pass/2 fail**, 09:19:13.988227–09:19:23.098100; literal-search/focus observations retained in `ui-focus-red` |
+| Second candidate | **85/8 PASS**, 09:20:11.574953–09:20:53.563271; 14 Documents cases, one new command case and existing consumers/i18n. App/browser TypeScript pass, but lint exits **1**, 09:20:45.648691–09:21:24.574780, on React Compiler `preserve-manual-memoization` at `selectedJobCompany`. Build does not execute after the failed gate. `ui-green-candidate-2` retains matching pre/post inputs and the complete failure |
+| Third candidate | **85/8 PASS**, 09:25:00.363258–09:25:29.018303; lint still exits **1**, 09:25:00.365982–09:25:23.137169. The second memo correction is not accepted or bypassed; `ui-green-candidate-3` retains its evidence |
+| First verified product gates | **85/8 PASS**, 09:29:20.378671–09:29:55.030600; zero failed/pending/todo cases. Fresh lint, app/browser TypeScript and production build pass; the later native sort regression is not yet known |
+| Batched-query RED | **14 pass/1 fail**, 16:00:27.318436–16:00:50.731485; new fifteenth Documents case reproduces the native sort loss. Its Back-then-edit coverage is strengthened after RED without removing existing assertions |
+| Query-repair affected GREEN | **86/8 PASS**, 16:02:37.386765–16:03:56.205205; 15 Documents cases, one new command case and existing consumers/i18n, with zero failed/pending/todo cases. Fresh lint, app/browser TypeScript and build all pass |
+| Post-layout affected GREEN | **86/8 PASS**, 16:13:44.632211–16:14:21.313668; same ordinary cases after the Alert-only layout correction, without a new behavioral assertion or waived check |
+
+Three retained single-file **stdin probes** at **09:28:11–09:28:17** isolate the compiler-lint boundary: original control exits 1, explicit primitive-number boundary exits 0 and inline-number validation exits 0. They leave product source unchanged and do not constitute runtime/product gates. The final fix makes the validated job ID's primitive dependency explicit and records why in a comment; no lint rule or runtime assertion is waived.
+
+The first passing product gates exit **0**: lint **09:29:20.370635–09:29:48.708668**, app TypeScript **09:29:48.711497–09:30:01.092933**, browser TypeScript **09:30:01.095630–09:30:06.979125**, build **09:30:06.981156–09:30:23.690851**. Export **`PP_o3OeJyY8xztbIpdIkR`** and held source are frozen in `ui-gates-verified/results.json`, SHA256 `e15d8adb8692b5e40840028b8662fcbe592985b4c83aa55c7fb3885d9d4d8d05`. This is the pre-query-repair export used by native attempts one and two. The prior Oct14 frontend build is not evidence for this interface.
+
+After the demonstrated query defect is repaired, gates exit **0**: lint **16:02:37.459197–16:03:42.357976**, app TypeScript **16:03:42.361579–16:04:16.198952**, browser TypeScript **16:04:16.204494–16:04:26.882705**, build **16:04:26.885246–16:04:38.121135**. Export **`bDQZjWQBSWlVswr35XRb8`** is retained with GREEN/source evidence in `ui-query-gates-verified/results.json`, SHA256 `bfacd743798172dc8d21358ce5c9ea1a1ded731557812fb059b73c78e27f3bf4`. This boundary precedes the later Alert layout correction.
+
+After image review and the Alert-only correction, fresh gates exit **0**: lint **16:11:40.238196–16:12:06.891058**, app TypeScript **16:12:06.894066–16:12:20.300785**, browser TypeScript **16:12:20.303828–16:12:26.675069**, build **16:12:26.677131–16:12:34.714164**. The latest export is **`H6tIi63meKxiKJUAiZrOm`**. Final gates, frontend 86/8, Documents and 17 PNGs are frozen in `ui-layout-documents-verified/results.json`, SHA256 `0d784e2d3881fa17d127c6e7052351698e95a7c2855557a520b6c97c1dc30571`. Matching `ui-alert-native-pre/post` retain all 29 source records plus unchanged API/test DLL and BUILD_ID hashes across final execution, including the affected unit run.
+
+## Native, visual and accessibility evidence
+
+The eight-case native specification covers actual local-cookie journeys for seven owned resource kinds/frozen provenance, server pagination/filter/history/job selection, initial503 Retry and incompatible held queries, stale-source Back/Refresh, exact attachment bytes/503/actual404, sign-out/A-B isolation, existing letter dirty Cancel/save, and finite bilingual/theme/reflow.
+
+First native attempt: **5 pass/3 fail**, **09:32:07.128874–09:32:49.679844**, with zero skipped/flaky cases and retries 0. `ui-native-attempt-1/results.json`, SHA256 `b5b9eab2a5a7f59b33d343e86825827973f01f3aaf835ca4a2a1ea8d031d1296`, retains the complete failures and source qualifications. Browser TypeScript passed immediately beforehand. This partial run is not final eight-case acceptance.
+
+| First-attempt fixture failure | Bounded specification correction |
+|---|---|
+| The Autocomplete fixture demands DOM focus on its option | Use the actual input-focused keyboard pattern: ArrowDown, prove `aria-activedescendant` identifies the visible option, then Enter. Keep exact selected-job and persisted URL/filter assertions |
+| A generic alert locator matches both the real CV Not Found alert and Next's route announcer | Locate the actual error alert; retain real source404, absent index, Back and explicit Refresh assertions |
+| Full-page capture changes scroll after the first 320px sample; focused-action y is **−1735.5** | Assert strict `0..viewport height` bounds before capture, explicitly restore scroll afterward and reassert the same bounds/focus; retain viewport screenshots. No relaxed reflow threshold or product change |
+
+The attempt reaches **six all-enabled axe scans / zero violations / 43 incomplete nodes**: six short notification badges and 37 overlapped contrast backgrounds. It retains **nine PNGs**. The responsive case stops after its first 320px sample, so the remaining seven widths are not evidenced by this attempt. Root has actually inspected only that sample's full-page and failed viewport images; no final visual acceptance is claimed. Full incomplete contexts remain pending contextual triage and manual qualifications.
+
+The first three corrections change only the eight-case specification. The **09:34→15:51 UTC usage pause** contains no inferred executions. Resumption compares all **29 captured sources** and recorded binaries unchanged against the first attempt's post-capture.
+
+Second native attempt: **7 pass/1 fail**, **15:55:09.478477–15:57:06.140335**, zero skipped/flaky cases and retries 0, retained in `ui-native-attempt-2`. All eight finite samples in C8 now pass strict focus, viewport overflow and enabled axe checks on the pre-query-repair export. C2 reaches its unchanged final server-query assertion and finds **`date-desc` where `name-asc` was selected**. This is a genuine product URL-update defect, preserved in the native evidence and reproduced by the new ordinary unit RED; its assertion is not weakened.
+
+That second attempt retains **14 axe scans / zero violations / 94 incomplete nodes**: 14 short badges, 79 overlapping backgrounds and one partially obscured contrast background, plus **17 PNGs**. These are finite observations of the previous export, not final visual/conformance acceptance.
+
+The prior **`bDQZjWQBSWlVswr35XRb8`** boundary passes **Documents 8/8**, **16:05:15.800503–16:06:35.882678**; **CV frame 4/4**, **16:06:35.893993–16:08:10.815285**; **ordinary P2-D01 1/1**, **16:08:10.827380–16:08:24.948002**. Its `ui-documents-verified/results.json` SHA256 is `032f284b7b2351cf39dbb2941d147dc42aa68295b579c25a690878a6a8cd4d73`. Latest **H6** checks independently pass **Documents 8/8**, **16:13:21.057245–16:14:18.605400**; **CV frame 4/4**, **16:14:18.614475–16:15:50.859320**; **ordinary P2-D01 1/1**, **16:15:50.869888–16:16:04.776459**. No skip, expected failure or retry conceals the repaired assertion. Latest consumer results and its extra PNG are frozen in `ui-layout-consumers-verified/results.json`, SHA256 `e8f2f5495f5f466183798d82c67de9839b151f1e4423a0088fa5e1886df47553`; previous-build passes are not substituted for these fresh 13 cases.
+
+All 17 **bDQ** PNGs are actually inspected by root/peer, producing the Alert layout finding above. Existing English CV `Not Found` copy in image 013 remains a P7-11 localisation finding. Final **H6** completes all eight samples and retains **15 default-enabled plus target-size axe scans / zero violations / 101 incompletes**. Root reads every context: 15 short badges, 85 form-background overlaps and one clipped Dashboard sidebar label in English/dark at 1024px. These remain open contrast/manual obligations, not passing judgments. Final images total **18: 17 Documents + 1 P2-D01**. Root actually views Documents 0–8 and the P2 image, confirms readable Alert text/action below; peer actually reviews Documents 9–16. Thus all 18 are reviewed, with `ui-layout-visual-peer-review.json` retaining the peer's receipt. The P2 full-page image has transitional overlay/sticky positioning; archive bytes/DOM assertions supply its contract evidence, and that image does not prove print fidelity or precise contrast. Finite image review supports the local UX decision without human accessibility acceptance.
+
+Setup deliberately captures a submitted package once through the established renderer to create real frozen evidence. That explicit fixture operation must remain separate from the assertion that Documents listing triggers no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Subsequent editor actions retain their existing owner reads and saves.
+
+Required finite samples remain 320, 360, 390, 768, 1024, 1280, 1440 and 1920px, including 1280×320 landscape, English/Bokmål and both themes. Retain every enabled axe/target-size violation and incomplete vector, record contextual triage and inspect actual PNGs before drawing visual conclusions. Human screen-reader, physical touch/software keyboard, real zoom and production fonts/photo qualification remain open; automation cannot close them.
+
+## Preservation and remaining work
+
+The additions derive frontend inventory **755/95** from **739/94**: 15 new Documents cases and one command case, one new suite. The earlier **754/95** inventory preceded the new query regression case. Eight new native cases pass the finite current-build run and derive browser inventory **312** from **304**; publication remains pending. Backend inventory stays **1,053**, inherited from the pushed contract. These are inventory calculations, not fresh full-suite pass counts; P7-12 retains the integrated acceptance run.
+
+All **57 PRES groups remain 56 PARTIAL / C11 DEFERRED**. Preserve the original 32 routes alongside the approved new route, owner/tenant authorization, canonical Career Profile data, CV overrides/revisions, one-job CV associations and immutable package content/files. Fresh P2-D01 passes as an ordinary case on latest H6; neither the earlier contract nor prior bDQ run is substituted for it.
+
+Final [static checks](evidence/ui-static-checks.json) pass at **2026-10-16 16:21 UTC**: secret scan, working/staged/phase whitespace, 19 documents / 518 local links, all57 groups and33 routes. Commit and verify the normal development push once remote access recovers. Retain every triaged contrast/manual qualification and the P7-11 missing-source copy finding. No actual MariaDB runtime, external provider delivery or fresh PDF corpus qualification is supplied by these UI tests. P5-13 remains withheld with **zero model qualification calls**; followMode stays deferred. Phase 8 is not implemented. **UI implemented-scope acceptance: CONDITIONAL GO locally; hygiene PASS, commit/push follow. P7-07 publication: pending. Production: NO-GO; main unchanged.**
diff --git a/docs/redesign/phase-7/p7-07/evidence/contract-results.json b/docs/redesign/phase-7/p7-07/evidence/contract-results.json
index 46359b3..2ef2327 100644
--- a/docs/redesign/phase-7/p7-07/evidence/contract-results.json
+++ b/docs/redesign/phase-7/p7-07/evidence/contract-results.json
@@ -3,7 +3,7 @@
"checkpoint": "P7-07",
"slice": "Backend metadata contract",
"recordedUtc": "2026-10-14T15:59:01.541142+00:00",
- "acceptance": "BACKEND_CONTRACT_IMPLEMENTED_SCOPE_CONDITIONAL_GO_HYGIENE_PASS_NORMAL_PUSH_PENDING",
+ "acceptance": "BACKEND_CONTRACT_IMPLEMENTED_SCOPE_CONDITIONAL_GO_HYGIENE_PASS_NORMAL_PUSH_VERIFIED",
"entry": {
"path": "docs/redesign/phase-7/p7-07/evidence/entry.json",
"bytes": 7037,
@@ -1306,5 +1306,19 @@
"Local links check target existence, not URL/heading anchors.",
"Static evidence is not runtime/manual/device/provider/production acceptance."
]
+ },
+ "normalPush": {
+ "commit": "8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be",
+ "observedUtc": "2026-10-14T16:02:14.215615+00:00",
+ "receipt": {
+ "path": ".artifacts/phase7/p7-07/contract-push-receipt.json",
+ "bytes": 2849,
+ "sha256": "cefd6e560dcbe62737898b11b1538e3c663835a745d4b1fc54f4dbdaa01b854c",
+ "hashPolicy": "Exact raw bytes"
+ },
+ "headOriginLiveEqual": true,
+ "workingTreeCleanAtObservation": true,
+ "mainUnchanged": "27657f31131cd95d42f0e1290853ec9c9b3959a4",
+ "qualification": "Successor observation recorded during the separate Documents UI boundary; not a new runtime test."
}
}
diff --git a/docs/redesign/phase-7/p7-07/evidence/ui-entry.json b/docs/redesign/phase-7/p7-07/evidence/ui-entry.json
new file mode 100644
index 0000000..396114a
--- /dev/null
+++ b/docs/redesign/phase-7/p7-07/evidence/ui-entry.json
@@ -0,0 +1,193 @@
+{
+ "schemaVersion": 1,
+ "checkpoint": "P7-07",
+ "slice": "Documents UI entry",
+ "recordedUtc": "2026-10-16T09:03:10.672833+00:00",
+ "status": "BASELINE_PASS_ORDINARY_RUNTIME_RED_CONFIRMED_IMPLEMENTATION_IN_PROGRESS",
+ "git": [
+ {
+ "command": [
+ "rtk",
+ "proxy",
+ "git",
+ "rev-parse",
+ "HEAD",
+ "origin/codex/ui-redesign-phase4",
+ "main",
+ "origin/main"
+ ],
+ "exitCode": 0,
+ "stdout": "8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be\n8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be\n27657f31131cd95d42f0e1290853ec9c9b3959a4\n27657f31131cd95d42f0e1290853ec9c9b3959a4\n",
+ "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n"
+ },
+ {
+ "command": [
+ "rtk",
+ "proxy",
+ "git",
+ "ls-remote",
+ "origin",
+ "refs/heads/codex/ui-redesign-phase4",
+ "refs/heads/main"
+ ],
+ "exitCode": 0,
+ "stdout": "8e4db121ae0ec3f1637c83ddeaa3f31abd0a61be\trefs/heads/codex/ui-redesign-phase4\n27657f31131cd95d42f0e1290853ec9c9b3959a4\trefs/heads/main\n",
+ "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n"
+ },
+ {
+ "command": [
+ "rtk",
+ "proxy",
+ "git",
+ "status",
+ "--porcelain=v1"
+ ],
+ "exitCode": 0,
+ "stdout": " M docs/redesign/phase-7/README.md\n M docs/redesign/phase-7/p7-07/CONTRACT.md\n M docs/redesign/phase-7/p7-07/README.md\n M docs/redesign/phase-7/p7-07/evidence/contract-results.json\n M job-tracker-ui/src/quick-command-preservation.test.tsx\n?? docs/redesign/phase-7/p7-07/UI.md\n?? job-tracker-ui/src/documents-page.test.tsx\n?? job-tracker-ui/src/views/DocumentsPage.tsx\n",
+ "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n"
+ }
+ ],
+ "precedingContractPush": {
+ "path": ".artifacts/phase7/p7-07/contract-push-receipt.json",
+ "bytes": 2849,
+ "sha256": "cefd6e560dcbe62737898b11b1538e3c663835a745d4b1fc54f4dbdaa01b854c",
+ "hashPolicy": "Exact raw bytes"
+ },
+ "baseline": {
+ "command": {
+ "command": [
+ "rtk",
+ "proxy",
+ "npm.cmd",
+ "test",
+ "--",
+ "--runInBand",
+ "--watchAll=false",
+ "--json",
+ "--outputFile=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\unit\\p707-ui-entry-20261016T085912Z.json",
+ "src/quick-command-preservation.test.tsx",
+ "src/app-shell-navigation.test.tsx",
+ "src/app-shell-notifications.test.tsx",
+ "src/cv-builder-page.test.tsx",
+ "src/attachments.test.tsx",
+ "src/application-assets.test.tsx"
+ ],
+ "startedUtc": "2026-10-16T08:59:12.556650+00:00",
+ "finishedUtc": "2026-10-16T08:59:53.028496+00:00",
+ "exitCode": 0,
+ "counts": {
+ "numPassedTests": 64,
+ "numFailedTests": 0,
+ "numPendingTests": 0,
+ "numTodoTests": 0,
+ "numPassedTestSuites": 6,
+ "numFailedTestSuites": 0
+ }
+ },
+ "raw": [
+ {
+ "path": ".artifacts/phase7/current/unit/p707-ui-entry-20261016T085912Z-command.json",
+ "bytes": 844,
+ "sha256": "57344b2fde556a2aed7efb9b97f698b0c94b286cbf69049b786ba2bee46115f4",
+ "hashPolicy": "Exact raw bytes"
+ },
+ {
+ "path": ".artifacts/phase7/current/unit/p707-ui-entry-20261016T085912Z.json",
+ "bytes": 26790,
+ "sha256": "47f82d530385901896babd31f0f5cd2753c5a2e13c683d751828d689e8419ae7",
+ "hashPolicy": "Exact raw bytes"
+ },
+ {
+ "path": ".artifacts/phase7/current/unit/p707-ui-entry-20261016T085912Z.log",
+ "bytes": 897,
+ "sha256": "cb29be40cdeabd697509875799d093bb14733f8ba3ab534a547cc688f3ec45b0",
+ "hashPolicy": "Exact raw bytes"
+ }
+ ]
+ },
+ "runtimeRed": {
+ "command": {
+ "command": [
+ "rtk",
+ "proxy",
+ "npm.cmd",
+ "test",
+ "--",
+ "--runInBand",
+ "--watchAll=false",
+ "--json",
+ "--outputFile=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\unit\\p707-documents-red-20261016T090057Z.json",
+ "src/documents-page.test.tsx"
+ ],
+ "startedUtc": "2026-10-16T09:00:57.645492+00:00",
+ "finishedUtc": "2026-10-16T09:01:47.462948+00:00",
+ "exitCode": 1,
+ "counts": {
+ "numPassedTests": 0,
+ "numFailedTests": 10,
+ "numPendingTests": 0,
+ "numTodoTests": 0,
+ "numPassedTestSuites": 0,
+ "numFailedTestSuites": 1
+ }
+ },
+ "raw": [
+ {
+ "path": ".artifacts/phase7/current/unit/p707-documents-red-20261016T090057Z-command.json",
+ "bytes": 640,
+ "sha256": "9005baefb5d555d0498d24f4c63cbc14d147f96eb49ed854b0c803bd60bb5bb5",
+ "hashPolicy": "Exact raw bytes"
+ },
+ {
+ "path": ".artifacts/phase7/current/unit/p707-documents-red-20261016T090057Z.json",
+ "bytes": 54903,
+ "sha256": "40f18821272dcadd8994edd4363c94cb3bd18b096d7081a4a633243dc288aa5f",
+ "hashPolicy": "Exact raw bytes"
+ },
+ {
+ "path": ".artifacts/phase7/current/unit/p707-documents-red-20261016T090057Z.log",
+ "bytes": 18630,
+ "sha256": "fbd51add874706321b10bc5dbd4b74c3cfd8792a80d8c3a484a5c64a946a5953",
+ "hashPolicy": "Exact raw bytes"
+ }
+ ],
+ "qualification": "Ten ordinary cases fail on absent rendered metadata/actions from the null-return page; no compile failure, skip or expected-failure marker."
+ },
+ "sourceBoundaries": [
+ {
+ "pre": {
+ "path": ".artifacts/phase7/p7-07/ui-entry-pre/source.json",
+ "bytes": 6985,
+ "sha256": "3f660b7b5aef07cc654cda186e1b3410d6e673b70da92e925422e11ad96ef029",
+ "hashPolicy": "Exact raw bytes"
+ },
+ "post": {
+ "path": ".artifacts/phase7/p7-07/ui-entry-post/source.json",
+ "bytes": 7363,
+ "sha256": "f4c1c2082c2cc1fab1932b76d24a9c9e8a3abf8b8f3dd792ac585b6c11e7a620",
+ "hashPolicy": "Exact raw bytes"
+ },
+ "changedExistingInputs": [],
+ "qualification": "All existing captured inputs remained byte-identical."
+ },
+ {
+ "pre": {
+ "path": ".artifacts/phase7/p7-07/ui-red-pre/source.json",
+ "bytes": 7338,
+ "sha256": "11d9c561bf358a4ea6ecd0a53e084b663f4ae95a6d4b46f5dc81d5492e3afcb6",
+ "hashPolicy": "Exact raw bytes"
+ },
+ "post": {
+ "path": ".artifacts/phase7/p7-07/ui-red-post/source.json",
+ "bytes": 7410,
+ "sha256": "bc853066d996fe30d22b9c8476d956d2670e721412ca436ded35fd59ecc03948",
+ "hashPolicy": "Exact raw bytes"
+ },
+ "changedExistingInputs": [
+ "job-tracker-ui/src/quick-command-preservation.test.tsx"
+ ],
+ "qualification": "Quick-command additions occurred separately during Documents-only RED and were not executed in that lane."
+ }
+ ],
+ "remaining": "UI implementation, affected regression, native/theme/responsive/axe and actual image review, builds, hygiene, commit and normal push. No full-suite/manual/provider/production acceptance."
+}
diff --git a/docs/redesign/phase-7/p7-07/evidence/ui-results.json b/docs/redesign/phase-7/p7-07/evidence/ui-results.json
new file mode 100644
index 0000000..dcd20a7
--- /dev/null
+++ b/docs/redesign/phase-7/p7-07/evidence/ui-results.json
@@ -0,0 +1,3507 @@
+{
+ "schemaVersion": 1,
+ "checkpoint": "P7-07",
+ "slice": "Documents interface",
+ "recordedUtc": "2026-10-16T16:17:51.836535+00:00",
+ "decisions": {
+ "implementedLocalScope": "CONDITIONAL GO",
+ "publication": "BLOCKED_REMOTE_UNREACHABLE_PUSH_NOT_YET_ATTEMPTED",
+ "wholeCheckpoint": "NOT_CLOSED_UNTIL_NORMAL_PUSH_AND_DIRECT_REMOTE_VERIFICATION",
+ "phase7": "ONGOING_P7_08_THROUGH_P7_12_REMAIN",
+ "production": "NO-GO",
+ "phase8Implementation": "NOT_STARTED"
+ },
+ "entry": {
+ "path": "docs/redesign/phase-7/p7-07/evidence/ui-entry.json",
+ "bytes": 7340,
+ "sha256": "324f1aa64b1e4dcdd5b387b3e760012f3acbe07aa564a57bae5e2c41589f6b9a",
+ "hashPolicy": "Exact raw bytes; source records also retain canonical-LF hashes."
+ },
+ "finalCapsules": [
+ {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/results.json",
+ "bytes": 135397,
+ "sha256": "0d784e2d3881fa17d127c6e7052351698e95a7c2855557a520b6c97c1dc30571",
+ "hashPolicy": "Exact raw bytes; source records also retain canonical-LF hashes."
+ },
+ {
+ "path": ".artifacts/phase7/p7-07/ui-layout-consumers-verified/results.json",
+ "bytes": 55406,
+ "sha256": "e8f2f5495f5f466183798d82c67de9839b151f1e4423a0088fa5e1886df47553",
+ "hashPolicy": "Exact raw bytes; source records also retain canonical-LF hashes."
+ }
+ ],
+ "sourceReconciliation": {
+ "before": {
+ "path": ".artifacts/phase7/p7-07/ui-alert-native-pre/source.json",
+ "bytes": 9553,
+ "sha256": "69b24a9b19ee7331e0749be9e200728db3f90ed90302a2023cf8d3117cd8f39f",
+ "hashPolicy": "Exact raw bytes; source records also retain canonical-LF hashes."
+ },
+ "after": {
+ "path": ".artifacts/phase7/p7-07/ui-alert-native-post/source.json",
+ "bytes": 9555,
+ "sha256": "fddb7912e9ffa44542552f96b42873facdcc896dffc0e3fc5ec180396300a72c",
+ "hashPolicy": "Exact raw bytes; source records also retain canonical-LF hashes."
+ },
+ "beforeUtc": "2026-10-16T16:13:20.448762+00:00",
+ "afterUtc": "2026-10-16T16:16:18.778381+00:00",
+ "records": 29,
+ "present": 28,
+ "absentOptional": "job-tracker-ui/src/documents.ts",
+ "sourcesUnchanged": true,
+ "binariesUnchanged": true,
+ "sources": [
+ {
+ "path": "job-tracker-ui/src/App.tsx",
+ "exists": true,
+ "bytes": 28639,
+ "sha256": "e0db89a88913d00f4fc4442a4b5ed8a5684b40c4cee336b239a2893ef08d9fe9",
+ "canonicalLfSha256": "66fb2e4166a942f130c945d997b5c84955592fc3b06780bdf5d3b5cdc563f4b3"
+ },
+ {
+ "path": "job-tracker-ui/src/api.ts",
+ "exists": true,
+ "bytes": 4948,
+ "sha256": "bec8dc665fef61f3c88938e9a61dda786a16008d6af9bc7688bdd615e7ba337c",
+ "canonicalLfSha256": "f75ac7be07a743396843ddad5fd3371e2d5fd3d9c5d933e09e26fae2159f2378"
+ },
+ {
+ "path": "job-tracker-ui/src/theme.ts",
+ "exists": true,
+ "bytes": 21733,
+ "sha256": "bcfc98ee692fda1d03ed23b952d2b2de35b16a45388bc43ad9885b6882df0302",
+ "canonicalLfSha256": "17b74ad41e4c3b41b0efa6a6b5296c326519383a9bcada40ca393ea3dcc912bf"
+ },
+ {
+ "path": "job-tracker-ui/src/sessionBoundary.ts",
+ "exists": true,
+ "bytes": 2248,
+ "sha256": "09edca77c98294818d2ad0c409ce595f1b6ca0c1f60419fc1078b70f96f5b7b9",
+ "canonicalLfSha256": "09edca77c98294818d2ad0c409ce595f1b6ca0c1f60419fc1078b70f96f5b7b9"
+ },
+ {
+ "path": "job-tracker-ui/src/views/DocumentsPage.tsx",
+ "exists": true,
+ "bytes": 26476,
+ "sha256": "737f5df5f2c00460f771f7a3b749897b54783b81ee9d9d2a801218ad9eeebc58",
+ "canonicalLfSha256": "737f5df5f2c00460f771f7a3b749897b54783b81ee9d9d2a801218ad9eeebc58"
+ },
+ {
+ "path": "job-tracker-ui/src/documents.ts",
+ "exists": false
+ },
+ {
+ "path": "job-tracker-ui/src/components/QuickCommandDialog.tsx",
+ "exists": true,
+ "bytes": 8462,
+ "sha256": "1e9872c1b3e0edbe48a7568e984c434eceea00384ebbb9731fe736c9d65fc64e",
+ "canonicalLfSha256": "7acf5b9a139de20d2b657cd7fdaa3a68c5b30e2aba6f9ee8a5c64009bf94fd30"
+ },
+ {
+ "path": "job-tracker-ui/src/layout/AppShell.tsx",
+ "exists": true,
+ "bytes": 30137,
+ "sha256": "cfb44f7ff806e0c659121ea518e8cb51797a63123fd79ee7d6fdb5372128c9a9",
+ "canonicalLfSha256": "a1462df25524963cdc84455c0a09e7de248b6e997b84c4a0a31dc24c99715e58"
+ },
+ {
+ "path": "job-tracker-ui/src/layout/PageLayout.tsx",
+ "exists": true,
+ "bytes": 2330,
+ "sha256": "9d7d8b3df0f533cc608c8b44519e12f1c9a8547fe91401c45023c6d2c34e111d",
+ "canonicalLfSha256": "31c7510446382039cd24b96e5709d5d8dfd00a0b84fd319a53183d1da6c2efa4"
+ },
+ {
+ "path": "job-tracker-ui/src/components/ViewStateNotice.tsx",
+ "exists": true,
+ "bytes": 2340,
+ "sha256": "fd4bb314346a1603d9e01a15fd02f01576b9fd959e83f8ff0b1dfe5ec94378e9",
+ "canonicalLfSha256": "7b6c3e6c311860897eb5327936dbb129e860781929cfd9a4d4e17f81c607c70c"
+ },
+ {
+ "path": "job-tracker-ui/src/hooks/useViewResource.ts",
+ "exists": true,
+ "bytes": 3292,
+ "sha256": "8c73a2fd845b8bc4c4c10b7c69f8d12dae8e83c434c537558a78b74377511b27",
+ "canonicalLfSha256": "5912131c525ba85ae6994efc38306a10ec2e673cc0082686226b59f00df5da99"
+ },
+ {
+ "path": "job-tracker-ui/src/i18n/I18nProvider.tsx",
+ "exists": true,
+ "bytes": 3184,
+ "sha256": "d22b5d033d0e405ffb5d581c752d61a24056d1e496f5f6a2f1baecef500f2113",
+ "canonicalLfSha256": "d22b5d033d0e405ffb5d581c752d61a24056d1e496f5f6a2f1baecef500f2113"
+ },
+ {
+ "path": "job-tracker-ui/src/i18n/translations.ts",
+ "exists": true,
+ "bytes": 327074,
+ "sha256": "c9ac8d576777c14a756f42b4722d9bf4532452bbcfea8851a0e522ae46f5461b",
+ "canonicalLfSha256": "c9ac8d576777c14a756f42b4722d9bf4532452bbcfea8851a0e522ae46f5461b"
+ },
+ {
+ "path": "job-tracker-ui/src/documents-page.test.tsx",
+ "exists": true,
+ "bytes": 28328,
+ "sha256": "ef5ce47720327112a239c699b36117da7990a91a1903830299e7631be71bde9a",
+ "canonicalLfSha256": "ef5ce47720327112a239c699b36117da7990a91a1903830299e7631be71bde9a"
+ },
+ {
+ "path": "job-tracker-ui/src/quick-command-preservation.test.tsx",
+ "exists": true,
+ "bytes": 8568,
+ "sha256": "ef08c0cadc3629864d562fab16527434a8441e5417f047d127a55d5db39bf6a4",
+ "canonicalLfSha256": "ef08c0cadc3629864d562fab16527434a8441e5417f047d127a55d5db39bf6a4"
+ },
+ {
+ "path": "job-tracker-ui/src/app-shell-navigation.test.tsx",
+ "exists": true,
+ "bytes": 3445,
+ "sha256": "b2bd174c967176f246487386a4a7b1f267ef5f390bed3d53d9be3ce9d3fe8abc",
+ "canonicalLfSha256": "5bb87f22a0c6f1f234bb052693c5611742274d3752a468c0e1be47eecc5b07d4"
+ },
+ {
+ "path": "job-tracker-ui/src/app-shell-notifications.test.tsx",
+ "exists": true,
+ "bytes": 3497,
+ "sha256": "a1f9d25b57915c2e096ca1a7ecff2a406d156adad8bbd3575b6ba3d069231b15",
+ "canonicalLfSha256": "5b1633bf5ae55ab7438d9936a625f5685fd75a510285645dfc1baf9dd0a95698"
+ },
+ {
+ "path": "job-tracker-ui/src/cv-builder-page.test.tsx",
+ "exists": true,
+ "bytes": 24180,
+ "sha256": "ef0b63ba95633cca81f1b391113a4ddbb27844574d4230c36e5f7de2e0218834",
+ "canonicalLfSha256": "ce2cab82555eef1d9c2adccdaa7472f0c037b2121026b09f9dc03c75be3a8d20"
+ },
+ {
+ "path": "job-tracker-ui/src/attachments.test.tsx",
+ "exists": true,
+ "bytes": 4078,
+ "sha256": "1519f67d3e3015d4a2709fa5d5bbc7da3b8208214e28cf20096b84192fdcadc5",
+ "canonicalLfSha256": "27e761c8d23b910712976b89c3ea6389a437ca1e756246f896883ba1aff859f2"
+ },
+ {
+ "path": "job-tracker-ui/src/application-assets.test.tsx",
+ "exists": true,
+ "bytes": 42930,
+ "sha256": "964a5f8352d451a7f8c5355aeaa79d54f4c687bb03c7ebc7c58324df7355a398",
+ "canonicalLfSha256": "964a5f8352d451a7f8c5355aeaa79d54f4c687bb03c7ebc7c58324df7355a398"
+ },
+ {
+ "path": "job-tracker-ui/src/i18n-provider.test.tsx",
+ "exists": true,
+ "bytes": 4869,
+ "sha256": "6bf5e80e721383290aef3b73ddaa31026d562596cc05d31712a7f1aef23ae824",
+ "canonicalLfSha256": "6bf5e80e721383290aef3b73ddaa31026d562596cc05d31712a7f1aef23ae824"
+ },
+ {
+ "path": "job-tracker-ui/src/profileCv.ts",
+ "exists": true,
+ "bytes": 12131,
+ "sha256": "190b3e15ab00741f1e6bea868a79c3ca221e059936406098b693d13f3ef3f1d7",
+ "canonicalLfSha256": "7e0b8aa13aa6df830f7e508c338c1e2dd995574bbfbd3233602654452f75cdb9"
+ },
+ {
+ "path": "job-tracker-ui/src/cvBuilder.ts",
+ "exists": true,
+ "bytes": 11587,
+ "sha256": "efff2eb126377ad6bc84a73d90c8a062be35789d1655ceba58aba359b2d82c36",
+ "canonicalLfSha256": "a202994e6f4f54127f1d8995e6ff87707e737dd2715324621d707cbfa17138cb"
+ },
+ {
+ "path": "job-tracker-ui/src/applicationWorkspace.ts",
+ "exists": true,
+ "bytes": 14534,
+ "sha256": "73b5b8c894e1ff053a41cc8c8beb6baf66ca03146abaec5866377602345f6981",
+ "canonicalLfSha256": "fe05a35a68924f1dbd15551d8cc3cfe2e0064449f91e07b4db085ed8d2be05b2"
+ },
+ {
+ "path": "job-tracker-ui/e2e/phase7-documents.spec.ts",
+ "exists": true,
+ "bytes": 41916,
+ "sha256": "e90a0ea766382d8a1c8242441a084625e0aff0d954c4fefcd3f757ffbdcd8690",
+ "canonicalLfSha256": "e90a0ea766382d8a1c8242441a084625e0aff0d954c4fefcd3f757ffbdcd8690"
+ },
+ {
+ "path": "job-tracker-ui/e2e/phase7-documents-contract.spec.ts",
+ "exists": true,
+ "bytes": 18346,
+ "sha256": "aca359ec044332ed688bd108352c28d6f4c960c8201dd4a7853b6281d341f483",
+ "canonicalLfSha256": "aca359ec044332ed688bd108352c28d6f4c960c8201dd4a7853b6281d341f483"
+ },
+ {
+ "path": "JobTrackerApi/Controllers/DocumentsController.cs",
+ "exists": true,
+ "bytes": 1711,
+ "sha256": "44633b84fbd33116e7f79f0fcd77cc8c15aea86d752464c6940b7dcd5506548b",
+ "canonicalLfSha256": "44633b84fbd33116e7f79f0fcd77cc8c15aea86d752464c6940b7dcd5506548b"
+ },
+ {
+ "path": "JobTrackerApi/Services/DocumentsIndexService.cs",
+ "exists": true,
+ "bytes": 14996,
+ "sha256": "def370bd0dbe90260e42800fded99302e56f8ac6b7693e73dd0a60cf0a3f7fd1",
+ "canonicalLfSha256": "def370bd0dbe90260e42800fded99302e56f8ac6b7693e73dd0a60cf0a3f7fd1"
+ },
+ {
+ "path": "JobTrackerApi/Program.cs",
+ "exists": true,
+ "bytes": 32013,
+ "sha256": "e906d23d8d4195a8e361428b26a9a713a77981f4d6636c80f68f6706c47e4d53",
+ "canonicalLfSha256": "6a548ddf7bc311840b931deaa89d21f75f01ac1f48d09699e4b5cd904f716432"
+ }
+ ],
+ "binaries": [
+ {
+ "path": "JobTrackerApi/bin/Release/net9.0/JobTrackerApi.dll",
+ "bytes": 4063232,
+ "sha256": "5bc23b88df160434f15b5cd09535144bdac3eeebc241d5e1837d854d936f4842",
+ "snapshot": ".artifacts/phase7/p7-07/ui-alert-native-post/binaries/JobTrackerApi.dll"
+ },
+ {
+ "path": "JobTrackerApi.Tests/bin/Release/net9.0/JobTrackerApi.Tests.dll",
+ "bytes": 1987072,
+ "sha256": "69f463b0a399e56fc774d0222c4f8e3a9207d3f6a8b878ef2fe58d46726cf3c6",
+ "snapshot": ".artifacts/phase7/p7-07/ui-alert-native-post/binaries/JobTrackerApi.Tests.dll"
+ },
+ {
+ "path": "job-tracker-ui/.next/BUILD_ID",
+ "bytes": 21,
+ "sha256": "e75f63f1ab30cccf9ba1cb1a0e43b96bc759ca592494a1e52da14d7ebd347f88",
+ "snapshot": ".artifacts/phase7/p7-07/ui-alert-native-post/binaries/BUILD_ID"
+ }
+ ],
+ "qualification": "Captured finite inputs and raw binary/build identities held through final 13 native cases and final 86 component tests. Four earlier successful build gates intentionally replace the prior BUILD_ID; no before/after build binary equality is claimed."
+ },
+ "build": {
+ "id": "H6tIi63meKxiKJUAiZrOm",
+ "exportCount": 76,
+ "exportDigest": "e0fc43a5fa1edbd02476ae4d7438dbdae6c0112297c3ff0686a9898078ba5dfc",
+ "policy": "Windows Path sort; original output path + TAB + exact raw file SHA + LF"
+ },
+ "frontend": {
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/unit/p707-ui-final-layout-20261016T161344Z.json",
+ "sha256": "78b95d5a1a9ffbd6f94051e50d90e24ab02aa6ed4ea6ba99e577d511c4268b59",
+ "bytes": 36727,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "startedUnixMs": 1792167225757,
+ "passed": 86,
+ "failed": 0,
+ "pending": 0,
+ "total": 86,
+ "suites": 8,
+ "cases": [
+ {
+ "name": "document package history is read-only and separates captured notes from answers",
+ "status": "passed"
+ },
+ {
+ "name": "cv section shows the attached variant and the ones available to attach",
+ "status": "passed"
+ },
+ {
+ "name": "attaching a different variant only re-points the application",
+ "status": "passed"
+ },
+ {
+ "name": "cv section points at the builder when there are no variants",
+ "status": "passed"
+ },
+ {
+ "name": "tailoring renders suggestions grouped by kind",
+ "status": "passed"
+ },
+ {
+ "name": "tailoring asks for a career profile when there is none",
+ "status": "passed"
+ },
+ {
+ "name": "submitted package capture appends an immutable history entry",
+ "status": "passed"
+ },
+ {
+ "name": "cover letter loads the current text and its history",
+ "status": "passed"
+ },
+ {
+ "name": "editing marks the draft dirty and saving sends the new text",
+ "status": "passed"
+ },
+ {
+ "name": "discarding returns to the saved text",
+ "status": "passed"
+ },
+ {
+ "name": "restoring an old version calls the restore endpoint",
+ "status": "passed"
+ },
+ {
+ "name": "an empty cover letter offers the template and an empty history",
+ "status": "passed"
+ },
+ {
+ "name": "the empty cover-letter template and rich-text controls follow Bokmål UI language",
+ "status": "passed"
+ },
+ {
+ "name": "AI cover letter suggestions use the linked CV and require explicit apply",
+ "status": "passed"
+ },
+ {
+ "name": "[PRES-W06] rejecting an AI cover letter preserves the saved draft without a write",
+ "status": "passed"
+ },
+ {
+ "name": "a failed load surfaces an error",
+ "status": "passed"
+ },
+ {
+ "name": "application answer and recruiter drafts save from the dedicated workspace",
+ "status": "passed"
+ },
+ {
+ "name": "application package drafts can be cleared without deleting ordinary notes",
+ "status": "passed"
+ },
+ {
+ "name": "failed cover-letter saving retains editable dirty text and original Save recovery across a live locale change",
+ "status": "passed"
+ },
+ {
+ "name": "failed application-answer saving keeps both dirty drafts editable and retries only the original save payload",
+ "status": "passed"
+ },
+ {
+ "name": "failed CV attachment leaves the current association and selector available for the original explicit action",
+ "status": "passed"
+ },
+ {
+ "name": "dirty version restore cancels without a write and confirmed held restore appends history exactly once",
+ "status": "passed"
+ },
+ {
+ "name": "held letter Save submits one snapshot and prevents an AI Apply from replacing its pending draft",
+ "status": "passed"
+ },
+ {
+ "name": "held AI generation locks its controls and preserves captured action attribution after a later action selection",
+ "status": "passed"
+ },
+ {
+ "name": "capture, detail and download failures keep owned frozen history and recover only through their original explicit actions",
+ "status": "passed"
+ },
+ {
+ "name": "all five asset read owners expose GET-only recovery without false empty history or locale refetch",
+ "status": "passed"
+ },
+ {
+ "name": "keyed job changes stop a late CV copy from attaching and never reuse another job's saved-copy recovery",
+ "status": "passed"
+ },
+ {
+ "name": "keyed job changes keep job B's answer drafts and callbacks independent of job A's late save",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] validates URL filters and reads one metadata page with literal Nordic search",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] server pagination and filter history preserve unknown query, hash and router state",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] literal search all remains free text and preserves unrelated navigation context",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] a batched sort selection and immediate search preserve both filters and navigation context",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] incompatible pending and failed queries hide old actions and late results cannot replace current metadata",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] Retry is one guarded GET and a completion after leaving cannot publish or start follow-up work",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] a true empty index, filtered no-match and request failure remain distinct",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] English and Bokmål presentation changes preserve query context without a metadata refetch",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] an out-of-range page offers explicit first-page recovery without claiming an empty index",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] seven resource kinds retain exact source actions, frozen provenance and honest date labels",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] attachment download is explicit, API-relative and single-flight with original MIME, filename and URL disposal",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] a completed download preserves deliberate Search focus instead of restoring the old button",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] download errors retry only that file and late download completion after leaving creates no URL",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] a published historical CV retains its percent-encoded Nordic public slug without fetching its body",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 documents] malformed external and mismatched resource paths never become source, public or download actions",
+ "status": "passed"
+ },
+ {
+ "name": "the CV library has a section heading and announces its held initial read without showing an empty result",
+ "status": "passed"
+ },
+ {
+ "name": "a failed CV list read has an explicit retry that clears the stale failure after recovery",
+ "status": "passed"
+ },
+ {
+ "name": "a live language change retains an open CV name and template draft without refetching library data",
+ "status": "passed"
+ },
+ {
+ "name": "held CV creation locks the draft and gallery, then keeps a failed draft for an explicit retry",
+ "status": "passed"
+ },
+ {
+ "name": "a locked CV template remains identifiable and cannot change the creation request",
+ "status": "passed"
+ },
+ {
+ "name": "a held duplicate blocks another action for the same CV and shows the persisted refreshed copy",
+ "status": "passed"
+ },
+ {
+ "name": "rename reads the current revision and preserves its settings when saving the new name",
+ "status": "passed"
+ },
+ {
+ "name": "a rename conflict keeps the original library entry and never retries the write automatically",
+ "status": "passed"
+ },
+ {
+ "name": "cancelling deletion returns focus to the surviving CV action control without removing the CV",
+ "status": "passed"
+ },
+ {
+ "name": "a failed creation keeps its draft and translates its failure after closing and reopening in another language",
+ "status": "passed"
+ },
+ {
+ "name": "generic PDF failure follows the current language both after failure and when a held request fails",
+ "status": "passed"
+ },
+ {
+ "name": "a held deletion preserves focus moved to another surviving control before the response arrives",
+ "status": "passed"
+ },
+ {
+ "name": "PDF status 503 shows an actionable message without retrying",
+ "status": "passed"
+ },
+ {
+ "name": "PDF status 504 shows an actionable message without retrying",
+ "status": "passed"
+ },
+ {
+ "name": "lists existing CVs from the variants API",
+ "status": "passed"
+ },
+ {
+ "name": "shows the empty state and creates a CV then navigates to the editor",
+ "status": "passed"
+ },
+ {
+ "name": "CV dashboard and create flow render in Bokmål",
+ "status": "passed"
+ },
+ {
+ "name": "CV deletion requires confirmation and supports cancellation",
+ "status": "passed"
+ },
+ {
+ "name": "[PRES-E07] command search opens Nordlys AS - Platform engineer at its existing destination",
+ "status": "passed"
+ },
+ {
+ "name": "[PRES-E07] command search opens Nordlys AS at its existing destination",
+ "status": "passed"
+ },
+ {
+ "name": "[PRES-E07 PRES-J04] the capture command opens review without creating a job, and closing clears search",
+ "status": "passed"
+ },
+ {
+ "name": "[P5-00 PRES-E07] command and empty results use valid list semantics",
+ "status": "passed"
+ },
+ {
+ "name": "[P5-02 PRES-E07] search failure is distinct from no matches and Retry recovers",
+ "status": "passed"
+ },
+ {
+ "name": "[P5-02 PRES-E09] closed and superseded searches cannot display stale account results",
+ "status": "passed"
+ },
+ {
+ "name": "[P5-02 PRES-E07] a slower old query cannot replace the newer matching results",
+ "status": "passed"
+ },
+ {
+ "name": "[P5-02 PRES-E07] arrows, Home, End and Enter activate one existing command; composition does not",
+ "status": "passed"
+ },
+ {
+ "name": "[P5-02 PRES-E07] arrowing from a focused result keeps focus and activation on the same item",
+ "status": "passed"
+ },
+ {
+ "name": "[P7-07 PRES-E07] the Documents command is localized and opens its owner index without fetching bodies",
+ "status": "passed"
+ },
+ {
+ "name": "[P5-01 PRES-E07 E08] grouped navigation exposes the current child without marking the parent as the page",
+ "status": "passed"
+ },
+ {
+ "name": "[P5-01 PRES-E07 E08] collapsed secondary destinations remain keyboard-operable",
+ "status": "passed"
+ },
+ {
+ "name": "[P5-01 PRES-E08] successful shell navigation focuses the new route heading and exposes a skip link",
+ "status": "passed"
+ },
+ {
+ "name": "attachments empty state renders drag and drop guidance",
+ "status": "passed"
+ },
+ {
+ "name": "attachments metadata controls update purpose and ai usage",
+ "status": "passed"
+ },
+ {
+ "name": "attachment failures remain explicit and retry restores the real empty state",
+ "status": "passed"
+ },
+ {
+ "name": "notification bell exposes its unread count and keyboard-accessible action",
+ "status": "passed"
+ },
+ {
+ "name": "shows build metadata only when the caller supplies the admin-only badge",
+ "status": "passed"
+ },
+ {
+ "name": "shell icon controls expose accessible names",
+ "status": "passed"
+ },
+ {
+ "name": "migrates the legacy Norwegian code and persists Bokmål using nb-NO",
+ "status": "passed"
+ },
+ {
+ "name": "hydrates an authenticated server preference without writing it back",
+ "status": "passed"
+ },
+ {
+ "name": "a failed account language save keeps the local choice and offers a localized retry: Norsk",
+ "status": "passed"
+ },
+ {
+ "name": "a failed account language save keeps the local choice and offers a localized retry: English",
+ "status": "passed"
+ },
+ {
+ "name": "a delayed language-save failure cannot offer a retry for a newer successful choice",
+ "status": "passed"
+ },
+ {
+ "name": "a previously delivered language retry cannot reverse a newer choice",
+ "status": "passed"
+ }
+ ],
+ "commandRaw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/unit/p707-ui-final-layout-20261016T161344Z-command.json",
+ "sha256": "3cc3a5dcb7554a0608d190677dd9f9de0a2129f30ef61a084000af162663c38e",
+ "bytes": 922,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "logRaw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/unit/p707-ui-final-layout-20261016T161344Z.log",
+ "sha256": "580816eabf1e8ae788682f0da835c07f3afbb82d022342140cd2913524c1f6d3",
+ "bytes": 1037,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "qualification": "Raw executed outcome; source bytes are not inferred retroactively. Passing subset is not slice acceptance."
+ },
+ "frontendCommand": {
+ "command": [
+ "rtk",
+ "proxy",
+ "npm.cmd",
+ "test",
+ "--",
+ "--runInBand",
+ "--watchAll=false",
+ "--json",
+ "--outputFile=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\unit\\p707-ui-final-layout-20261016T161344Z.json",
+ "src/documents-page.test.tsx",
+ "src/quick-command-preservation.test.tsx",
+ "src/app-shell-navigation.test.tsx",
+ "src/app-shell-notifications.test.tsx",
+ "src/cv-builder-page.test.tsx",
+ "src/attachments.test.tsx",
+ "src/application-assets.test.tsx",
+ "src/i18n-provider.test.tsx"
+ ],
+ "startedUtc": "2026-10-16T16:13:44.632211+00:00",
+ "finishedUtc": "2026-10-16T16:14:21.313668+00:00",
+ "exitCode": 0,
+ "counts": {
+ "numPassedTests": 86,
+ "numFailedTests": 0,
+ "numPendingTests": 0,
+ "numTodoTests": 0,
+ "numPassedTestSuites": 8,
+ "numFailedTestSuites": 0
+ }
+ },
+ "native": {
+ "documents": {
+ "command": {
+ "name": "documents",
+ "command": [
+ "rtk",
+ "proxy",
+ "node",
+ "node_modules/@playwright/test/cli.js",
+ "test",
+ "e2e/phase7-documents.spec.ts",
+ "--retries=0",
+ "--reporter=list,json",
+ "--output=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\ui\\documents-results"
+ ],
+ "startedUtc": "2026-10-16T16:13:21.057245+00:00",
+ "finishedUtc": "2026-10-16T16:14:18.605400+00:00",
+ "exitCode": 0
+ },
+ "commandRaw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents-command.json",
+ "sha256": "eea0079fdf6e5ea082534aef045e89b09bae43215585f6b30ba202ba41421ee2",
+ "bytes": 470,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "reportRaw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "logRaw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.log",
+ "sha256": "cb3f69b833485db84a72b9d1e1aca0ac0148b85d55e31fa826b70dcaa6bed5df",
+ "bytes": 1948,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "stats": {
+ "startTime": "2026-10-16T16:13:21.642Z",
+ "duration": 56883.096,
+ "expected": 8,
+ "skipped": 0,
+ "unexpected": 0,
+ "flaky": 0
+ },
+ "cases": [
+ {
+ "title": "[P7-07 Documents] seven owned metadata kinds retain honest source actions and frozen provenance after current source deletion",
+ "file": "phase7-documents.spec.ts",
+ "line": 76,
+ "expectedStatus": "passed",
+ "project": "chromium",
+ "resultStatuses": [
+ "passed"
+ ],
+ "retries": [
+ 0
+ ],
+ "errors": [
+ []
+ ],
+ "durationMs": 5051
+ },
+ {
+ "title": "[P7-07 Documents] server pages filter history owned job selection and reload preserve unknown query and hash",
+ "file": "phase7-documents.spec.ts",
+ "line": 85,
+ "expectedStatus": "passed",
+ "project": "chromium",
+ "resultStatuses": [
+ "passed"
+ ],
+ "retries": [
+ 0
+ ],
+ "errors": [
+ []
+ ],
+ "durationMs": 6444
+ },
+ {
+ "title": "[P7-07 Documents] initial503 Retry and incompatible held query expose no stale actions or false empty state",
+ "file": "phase7-documents.spec.ts",
+ "line": 92,
+ "expectedStatus": "passed",
+ "project": "chromium",
+ "resultStatuses": [
+ "passed"
+ ],
+ "retries": [
+ 0
+ ],
+ "errors": [
+ []
+ ],
+ "durationMs": 2619
+ },
+ {
+ "title": "[P7-07 Documents] stale CV opens the existing missing-source error and Back plus explicit Refresh removes its metadata",
+ "file": "phase7-documents.spec.ts",
+ "line": 100,
+ "expectedStatus": "passed",
+ "project": "chromium",
+ "resultStatuses": [
+ "passed"
+ ],
+ "retries": [
+ 0
+ ],
+ "errors": [
+ []
+ ],
+ "durationMs": 2409
+ },
+ {
+ "title": "[P7-07 Documents] explicit authenticated attachment GET retries exact bytes and a real404 offers only list Refresh",
+ "file": "phase7-documents.spec.ts",
+ "line": 106,
+ "expectedStatus": "passed",
+ "project": "chromium",
+ "resultStatuses": [
+ "passed"
+ ],
+ "retries": [
+ 0
+ ],
+ "errors": [
+ []
+ ],
+ "durationMs": 3169
+ },
+ {
+ "title": "[P7-07 Documents] actual sign-out clears private rows and a separate owner sees only its own metadata",
+ "file": "phase7-documents.spec.ts",
+ "line": 112,
+ "expectedStatus": "passed",
+ "project": "chromium",
+ "resultStatuses": [
+ "passed"
+ ],
+ "retries": [
+ 0
+ ],
+ "errors": [
+ []
+ ],
+ "durationMs": 3862
+ },
+ {
+ "title": "[P7-07 Documents] existing letter source keeps dirty edits on navigation Cancel and reindexes only the explicit saved revision",
+ "file": "phase7-documents.spec.ts",
+ "line": 117,
+ "expectedStatus": "passed",
+ "project": "chromium",
+ "resultStatuses": [
+ "passed"
+ ],
+ "retries": [
+ 0
+ ],
+ "errors": [
+ []
+ ],
+ "durationMs": 3991
+ },
+ {
+ "title": "[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors",
+ "file": "phase7-documents.spec.ts",
+ "line": 123,
+ "expectedStatus": "passed",
+ "project": "chromium",
+ "resultStatuses": [
+ "passed"
+ ],
+ "retries": [
+ 0
+ ],
+ "errors": [
+ []
+ ],
+ "durationMs": 20815
+ }
+ ],
+ "reportErrors": [],
+ "configuration": {
+ "version": "1.62.1",
+ "workers": 1,
+ "projects": [
+ {
+ "name": "chromium",
+ "retries": 0
+ }
+ ]
+ },
+ "audit": {
+ "scans": [
+ {
+ "test": "[P7-07 Documents] seven owned metadata kinds retain honest source actions and frozen provenance after current source deletion",
+ "attachment": "p707-documents-en-light-1440-seven-honest-metadata-kinds-audit",
+ "geometry": {
+ "width": 1440,
+ "height": 900,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "BODY",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/001-p707-documents-en-light-1440-seven-honest-metadata-kinds-audit.json",
+ "sha256": "01f9e756f761309e66fce4d3f7095ab96f7a7a71e495eec800eb94cffc0712e1",
+ "bytes": 21055,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "light",
+ "width": 1440,
+ "height": 900
+ },
+ "state": "seven-honest-metadata-kinds",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] server pages filter history owned job selection and reload preserve unknown query and hash",
+ "attachment": "p707-documents-en-light-1440-filtered-server-page-reloaded-audit",
+ "geometry": {
+ "width": 1440,
+ "height": 900,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "BODY",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/005-p707-documents-en-light-1440-filtered-server-page-reloaded-audit.json",
+ "sha256": "68da29168b014197db2812ca0b227c1923a7c6605dc0de4dd5d6acdb1e9804e2",
+ "bytes": 15240,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "light",
+ "width": 1440,
+ "height": 900
+ },
+ "state": "filtered-server-page-reloaded",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] initial503 Retry and incompatible held query expose no stale actions or false empty state",
+ "attachment": "p707-documents-en-light-1440-latest-query-after-get-recovery-audit",
+ "geometry": {
+ "width": 1440,
+ "height": 900,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "INPUT",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/008-p707-documents-en-light-1440-latest-query-after-get-recovery-audit.json",
+ "sha256": "3fe32f65decbaa08b23501bc07687b59a9eb0c237fc72f41121f409073e90b14",
+ "bytes": 14919,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "light",
+ "width": 1440,
+ "height": 900
+ },
+ "state": "latest-query-after-get-recovery",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] stale CV opens the existing missing-source error and Back plus explicit Refresh removes its metadata",
+ "attachment": "p707-documents-en-light-1440-existing-missing-source-editor-audit",
+ "geometry": {
+ "width": 1440,
+ "height": 900,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "BODY",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 1,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/012-p707-documents-en-light-1440-existing-missing-source-editor-audit.json",
+ "sha256": "4e5d36d7069d9851a95661f8db3dd2bc2bdea97da17643553ab02d2369c9dac9",
+ "bytes": 7494,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "light",
+ "width": 1440,
+ "height": 900
+ },
+ "state": "existing-missing-source-editor",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 1
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] explicit authenticated attachment GET retries exact bytes and a real404 offers only list Refresh",
+ "attachment": "p707-documents-en-light-1440-real-deleted-attachment-stale-metadata-audit",
+ "geometry": {
+ "width": 1440,
+ "height": 900,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "BUTTON",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 8,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/015-p707-documents-en-light-1440-real-deleted-attachment-stale-metadata-audit.json",
+ "sha256": "c34622ecd615d5c366ae6abe43356c3962b135e2c2ff39fc65e16a4a67067f9f",
+ "bytes": 15735,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "light",
+ "width": 1440,
+ "height": 900
+ },
+ "state": "real-deleted-attachment-stale-metadata",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 8
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] actual sign-out clears private rows and a separate owner sees only its own metadata",
+ "attachment": "p707-documents-en-light-1440-second-owner-no-cached-private-rows-audit",
+ "geometry": {
+ "width": 1440,
+ "height": 900,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "BODY",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/018-p707-documents-en-light-1440-second-owner-no-cached-private-rows-audit.json",
+ "sha256": "8cd2c8c7b377cb31cb083d6e37274aa1a73fe370638b64dbc069b2c1eaed87b0",
+ "bytes": 14888,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "light",
+ "width": 1440,
+ "height": 900
+ },
+ "state": "second-owner-no-cached-private-rows",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] existing letter source keeps dirty edits on navigation Cancel and reindexes only the explicit saved revision",
+ "attachment": "p707-documents-en-light-1440-existing-letter-dirty-navigation-cancel-audit",
+ "geometry": {
+ "width": 1440,
+ "height": 900,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "DIV",
+ "role": "button",
+ "name": "Documents"
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/021-p707-documents-en-light-1440-existing-letter-dirty-navigation-cancel-audit.json",
+ "sha256": "ab02ba6421170e2616a95aa6538fcb8f14f6bb8fe8808862841801687bb1d774",
+ "bytes": 15446,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "light",
+ "width": 1440,
+ "height": 900
+ },
+ "state": "existing-letter-dirty-navigation-cancel",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors",
+ "attachment": "p707-documents-en-light-320-long-metadata-keyboard-audit",
+ "geometry": {
+ "width": 320,
+ "height": 740,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "A",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/024-p707-documents-en-light-320-long-metadata-keyboard-audit.json",
+ "sha256": "ca0fba4f15d20eabdae7200fe9b13d41fbe80d4df7f66c8c923758a2b6172d51",
+ "bytes": 16879,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "light",
+ "width": 320,
+ "height": 740
+ },
+ "state": "long-metadata-keyboard",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors",
+ "attachment": "p707-documents-nb-dark-360-long-metadata-keyboard-audit",
+ "geometry": {
+ "width": 360,
+ "height": 844,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "A",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/028-p707-documents-nb-dark-360-long-metadata-keyboard-audit.json",
+ "sha256": "a694349cf92d7e45046ceb560c51318787ef1339270a2fc4f3bcfb489bd5d1f7",
+ "bytes": 17188,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "nb",
+ "theme": "dark",
+ "width": 360,
+ "height": 844
+ },
+ "state": "long-metadata-keyboard",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors",
+ "attachment": "p707-documents-en-dark-390-long-metadata-keyboard-audit",
+ "geometry": {
+ "width": 390,
+ "height": 844,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "A",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/031-p707-documents-en-dark-390-long-metadata-keyboard-audit.json",
+ "sha256": "2f4d484be339211c3d20391ead935a15cec4e5fec0da5d0b50e3b4bc1ea5ad3a",
+ "bytes": 16878,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "dark",
+ "width": 390,
+ "height": 844
+ },
+ "state": "long-metadata-keyboard",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors",
+ "attachment": "p707-documents-nb-light-768-long-metadata-keyboard-audit",
+ "geometry": {
+ "width": 768,
+ "height": 1024,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "A",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/034-p707-documents-nb-light-768-long-metadata-keyboard-audit.json",
+ "sha256": "e4247bde6a87329f9c1c90e90b14dab2d7fe2e251f801097f85c5e7b25f586fd",
+ "bytes": 17190,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "nb",
+ "theme": "light",
+ "width": 768,
+ "height": 1024
+ },
+ "state": "long-metadata-keyboard",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors",
+ "attachment": "p707-documents-en-dark-1024-long-metadata-keyboard-audit",
+ "geometry": {
+ "width": 1024,
+ "height": 768,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "A",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 8,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/037-p707-documents-en-dark-1024-long-metadata-keyboard-audit.json",
+ "sha256": "178fd2e85306a3bd4c05a8a671fd09c3e0cb5c5f92b6bd123cefeeceb0648568",
+ "bytes": 24988,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "dark",
+ "width": 1024,
+ "height": 768
+ },
+ "state": "long-metadata-keyboard",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 8
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors",
+ "attachment": "p707-documents-nb-light-1280-long-metadata-keyboard-audit",
+ "geometry": {
+ "width": 1280,
+ "height": 320,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "A",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/040-p707-documents-nb-light-1280-long-metadata-keyboard-audit.json",
+ "sha256": "bb2c33524519f23c0e52dda4b86d5d627eda0904f11d0e42a90fb6487a65108b",
+ "bytes": 24504,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "nb",
+ "theme": "light",
+ "width": 1280,
+ "height": 320
+ },
+ "state": "long-metadata-keyboard",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors",
+ "attachment": "p707-documents-en-light-1440-long-metadata-keyboard-audit",
+ "geometry": {
+ "width": 1440,
+ "height": 900,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "A",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/044-p707-documents-en-light-1440-long-metadata-keyboard-audit.json",
+ "sha256": "ce88a5e3e2ac5c4ab89f36b60145ac7473f796a63e4f94640f7a61b027d6b6e8",
+ "bytes": 23800,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "en",
+ "theme": "light",
+ "width": 1440,
+ "height": 900
+ },
+ "state": "long-metadata-keyboard",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ },
+ {
+ "test": "[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors",
+ "attachment": "p707-documents-nb-dark-1920-long-metadata-keyboard-audit",
+ "geometry": {
+ "width": 1920,
+ "height": 1080,
+ "rootOverflow": 0,
+ "focused": {
+ "tag": "A",
+ "role": null,
+ "name": null
+ },
+ "overflowElements": []
+ },
+ "engine": {
+ "name": "axe-core",
+ "version": "4.13.0"
+ },
+ "toolOptions": {
+ "rules": {
+ "target-size": {
+ "enabled": true
+ }
+ },
+ "reporter": "v1"
+ },
+ "violationNodes": 0,
+ "incompleteNodes": 7,
+ "raw": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/embedded/documents/047-p707-documents-nb-dark-1920-long-metadata-keyboard-audit.json",
+ "sha256": "89b2795e52ca5c89dcb28dfe90c66e54cd401b089c7f51133defe9737a988ad3",
+ "bytes": 24459,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "originalEmbeddedReport": {
+ "path": ".artifacts/phase7/p7-07/ui-layout-documents-verified/documents.json",
+ "sha256": "942d7ce85bc7baea27ab37ff7229dcd407307d9921aa64ac95fc14b68a80621c",
+ "bytes": 3719823,
+ "hashPolicy": "Exact measured raw bytes"
+ },
+ "context": {
+ "sample": {
+ "language": "nb",
+ "theme": "dark",
+ "width": 1920,
+ "height": 1080
+ },
+ "state": "long-metadata-keyboard",
+ "boundary": "Actual Program/local Identity cookie sessions, CSRF-protected existing source writes and owner Documents GET use disposable isolated SQLite. Setup creates seven genuine existing resource kinds; an explicit setup submitted-package capture invokes the established CV renderer once to create its immutable snapshot. Documents listing itself must issue no source-body/render/preview/PDF/export/AI/provider/send request or mutation. Existing source editors own their own reads, errors, dirty guards and saves after deliberate navigation. Attachment download is an explicit authenticated existing GET; controlled precommit503 qualifies read recovery, not uncertain delivery. Frozen package metadata/archive bytes are independently read after current CV/upload deletion. No provider grant/send/sync/import/model, raw SQL/schema, storage relocation, production/main/deployment or Phase8 action. Free accounts and disabled workers remain mandatory; external/model/delivery browser requests are blocked and recorded without credentials/CSRF/session headers. Exact27-field metadata and finite all-enabled axe/target-size vectors retain every incomplete node. UI evidence does not establish internal SQL/service-call counts, live MariaDB, human screen reader, physical touch/software keyboard, real zoom, production fonts/photos, provider qualification or production readiness. Correspondence opens its existing context without exact-ID Resume; Submission opens history without automatically selecting a package; CV PDF stays the existing explicit export operation.",
+ "qualifications": [
+ "Finite local Chromium states; not the full width/language/theme/state cross product.",
+ "All incomplete nodes retained for triage; no human AT, physical device/software keyboard, real zoom, precise screenshot contrast or production font/photo acceptance.",
+ "Full-page PNG can be resized and sticky elements captured out of viewport; focus viewport images are separate.",
+ "Actual MariaDB, model/provider/delivery and production release remain unverified."
+ ]
+ },
+ "rules": [
+ {
+ "kind": "incomplete",
+ "id": "color-contrast",
+ "nodes": 7
+ }
+ ]
+ }
+ ],
+ "violationNodes": 0,
+ "incompleteNodes": 101,
+ "checkContexts": [
+ {
+ "kind": "incomplete",
+ "rule": "color-contrast",
+ "check": "color-contrast",
+ "message": "Element content is too short to determine if it is actual text content",
+ "occurrences": 15,
+ "representativeContexts": [
+ {
+ "attachment": "p707-documents-en-light-1440-seven-honest-metadata-kinds-audit",
+ "target": [
+ ".MuiBadge-badge"
+ ],
+ "data": {
+ "contrastRatio": 0,
+ "fontSize": "9.0pt (12px)",
+ "fontWeight": "normal",
+ "messageKey": "shortTextContent",
+ "expectedContrastRatio": "4.5:1"
+ },
+ "relatedNodes": []
+ },
+ {
+ "attachment": "p707-documents-en-light-1440-filtered-server-page-reloaded-audit",
+ "target": [
+ ".MuiBadge-badge"
+ ],
+ "data": {
+ "contrastRatio": 0,
+ "fontSize": "9.0pt (12px)",
+ "fontWeight": "normal",
+ "messageKey": "shortTextContent",
+ "expectedContrastRatio": "4.5:1"
+ },
+ "relatedNodes": []
+ }
+ ]
+ },
+ {
+ "kind": "incomplete",
+ "rule": "color-contrast",
+ "check": "color-contrast",
+ "message": "Element's background color could not be determined because it is overlapped by another element",
+ "occurrences": 85,
+ "representativeContexts": [
+ {
+ "attachment": "p707-documents-en-light-1440-seven-honest-metadata-kinds-audit",
+ "target": [
+ "#_r_r_"
+ ],
+ "data": {
+ "contrastRatio": 0,
+ "fontSize": "10.5pt (14px)",
+ "fontWeight": "normal",
+ "messageKey": "bgOverlap",
+ "expectedContrastRatio": "4.5:1"
+ },
+ "relatedNodes": []
+ },
+ {
+ "attachment": "p707-documents-en-light-1440-seven-honest-metadata-kinds-audit",
+ "target": [
+ "div[aria-labelledby=\"_r_p_-type\"]"
+ ],
+ "data": {
+ "contrastRatio": 0,
+ "fontSize": "10.5pt (14px)",
+ "fontWeight": "normal",
+ "messageKey": "bgOverlap",
+ "expectedContrastRatio": "4.5:1"
+ },
+ "relatedNodes": []
+ }
+ ]
+ },
+ {
+ "kind": "incomplete",
+ "rule": "color-contrast",
+ "check": "color-contrast",
+ "message": "Element's background color could not be determined because it's partially obscured by another element",
+ "occurrences": 1,
+ "representativeContexts": [
+ {
+ "attachment": "p707-documents-en-dark-1024-long-metadata-keyboard-audit",
+ "target": [
+ ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(1) > .MuiList-padding.css-k1qkeu[aria-label=\"Navigation\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-j56oom.MuiListItemButton-root[aria-label=\"Dashboard\"] > .MuiListItemText-root.css-a2y5gf > .css-1e345qr.MuiTypography-body1.MuiListItemText-primary"
+ ],
+ "data": {
+ "contrastRatio": 0,
+ "fontSize": "10.5pt (14px)",
+ "fontWeight": "normal",
+ "messageKey": "elmPartiallyObscured",
+ "expectedContrastRatio": "4.5:1"
+ },
+ "relatedNodes": [
+ {
+ "html": "
} />);
+}
+
+function location() { return JSON.parse(screen.getByTestId('documents-location').textContent!) as { pathname: string; search: string; hash: string; state: unknown }; }
+function reads() { return mockedApi.get.mock.calls.filter(([url]) => url === '/documents'); }
+function row(name: string) { return within(screen.getByText(name, { exact: true }).closest('tr, article') as HTMLElement); }
+async function select(labelKey: string, optionKey: string) { fireEvent.mouseDown(screen.getByRole('combobox', { name: copy(labelKey) })); fireEvent.click(await screen.findByRole('option', { name: copy(optionKey) })); }
+async function language(value: UiLanguage) { await act(async () => { window.dispatchEvent(new StorageEvent('storage', { key: 'uiLanguage', newValue: value })); }); }
+
+beforeEach(() => {
+ localStorage.clear();
+ document.documentElement.lang = 'en';
+ jest.clearAllMocks();
+ mockedApi.get.mockReset().mockImplementation((url: string) => url === '/documents'
+ ? Promise.resolve({ data: page() } as never)
+ : Promise.reject(new Error(`Unexpected read ${url}`)));
+ for (const method of [mockedApi.post, mockedApi.put, mockedApi.patch, mockedApi.delete]) method.mockReset().mockRejectedValue(new Error('Unexpected document-index write'));
+ Object.defineProperty(URL, 'createObjectURL', { configurable: true, writable: true, value: jest.fn(() => 'blob:uploaded-file') });
+ Object.defineProperty(URL, 'revokeObjectURL', { configurable: true, writable: true, value: jest.fn() });
+});
+
+afterEach(() => {
+ expect(mockedApi.post).not.toHaveBeenCalled();
+ expect(mockedApi.put).not.toHaveBeenCalled();
+ expect(mockedApi.patch).not.toHaveBeenCalled();
+ expect(mockedApi.delete).not.toHaveBeenCalled();
+ jest.restoreAllMocks();
+});
+
+test('[P7-07 documents] validates URL filters and reads one metadata page with literal Nordic search', async () => {
+ renderPage('/documents?q=100%25_%21+%C3%86%C3%98%C3%85&page=bad&type=unknown&state=unknown&visibility=unknown&sort=unknown&jobApplicationId=-2&keep=1#context');
+ expect(await screen.findByText('Metadata CV ÆØÅ', { exact: true })).toBeVisible();
+ expect(reads()).toHaveLength(1);
+ expect(reads()[0][1]?.params).toEqual({ ...defaults, q: '100%_! ÆØÅ' });
+ expect(screen.getByRole('textbox', { name: copy('documentsSearch') })).toHaveValue('100%_! ÆØÅ');
+ expect(mockedApi.get.mock.calls.map(([url]) => url)).toEqual(['/documents']);
+ expect(URL.createObjectURL).not.toHaveBeenCalled();
+});
+
+test('[P7-07 documents] server pagination and filter history preserve unknown query, hash and router state', async () => {
+ mockedApi.get.mockImplementation((_url: string, config?: any) => Promise.resolve({ data: page([item({ name: `Page ${config.params.page} ${config.params.type}` })], config.params.page, 26) } as never));
+ const state = { from: '/jobs/42', draftContext: 'preserve-navigation-context' };
+ renderPage('/documents?page=2&jobApplicationId=42&keep=%C3%A6%26x#materials', state);
+ expect(await screen.findByText('Page 2 all')).toBeVisible();
+ expect(reads()[0][1]?.params).toEqual({ ...defaults, page: 2, jobApplicationId: 42 });
+ await select('documentsType', 'documentsTypeCv');
+ expect(await screen.findByText('Page 1 cv')).toBeVisible();
+ const current = location(); const query = new URLSearchParams(current.search);
+ expect(query.get('keep')).toBe('æ&x'); expect(query.get('jobApplicationId')).toBe('42'); expect(query.get('page')).not.toBe('2');
+ expect(current.hash).toBe('#materials'); expect(current.state).toEqual(state);
+ expect(reads().at(-1)?.[1]?.params).toEqual({ ...defaults, type: 'cv', jobApplicationId: 42 });
+ fireEvent.click(screen.getByRole('button', { name: 'History Back' }));
+ expect(await screen.findByText('Page 2 all')).toBeVisible();
+ expect(reads().at(-1)?.[1]?.params).toEqual({ ...defaults, page: 2, jobApplicationId: 42 });
+});
+
+test('[P7-07 documents] literal search all remains free text and preserves unrelated navigation context', async () => {
+ mockedApi.get.mockImplementation((_url: string, config?: any) => Promise.resolve({ data: page([item({ name: config.params.q === 'all' ? 'Literal all metadata' : 'Initial metadata' })]) } as never));
+ const state = { from: '/jobs/42', draftContext: 'preserve-literal-search-context' };
+ renderPage('/documents?keep=%C3%A6%26x#materials', state);
+ await screen.findByText('Initial metadata');
+ const search = screen.getByRole('textbox', { name: copy('documentsSearch') });
+ fireEvent.change(search, { target: { value: 'all' } });
+ expect(search).toHaveValue('all');
+ expect(await screen.findByText('Literal all metadata')).toBeVisible();
+ expect(mockedApi.get.mock.calls.map(([url]) => url)).toEqual(['/documents', '/documents']);
+ expect(reads()[1][1]?.params).toEqual({ ...defaults, q: 'all' });
+ const current = location(); const query = new URLSearchParams(current.search);
+ expect(query.get('q')).toBe('all'); expect(query.get('keep')).toBe('æ&x');
+ expect(current.hash).toBe('#materials'); expect(current.state).toEqual(state);
+});
+
+test('[P7-07 documents] a batched sort selection and immediate search preserve both filters and navigation context', async () => {
+ mockedApi.get.mockImplementation((_url: string, config?: any) => Promise.resolve({ data: page([item({ name: config.params.q === 'Indexed' && config.params.sort === 'name-asc' ? 'Indexed sorted metadata' : 'Initial metadata' })]) } as never));
+ const state = { from: '/jobs/42', draftContext: 'preserve-rapid-filter-context' };
+ renderPage('/documents?keep=%C3%A6%26x#context', state);
+ await screen.findByText('Initial metadata');
+ const search = screen.getByRole('textbox', { name: copy('documentsSearch') });
+ fireEvent.mouseDown(screen.getByRole('combobox', { name: copy('documentsSort') }));
+ const option = await screen.findByRole('option', { name: copy('documentsSortNameAsc') });
+ act(() => { fireEvent.click(option); fireEvent.change(search, { target: { value: 'Indexed' } }); });
+ const current = location(); const query = new URLSearchParams(current.search);
+ expect(query.get('sort')).toBe('name-asc'); expect(query.get('q')).toBe('Indexed');
+ expect(query.get('keep')).toBe('æ&x'); expect(current.hash).toBe('#context'); expect(current.state).toEqual(state);
+ expect(await screen.findByText('Indexed sorted metadata')).toBeVisible();
+ expect(reads().at(-1)?.[1]?.params).toEqual({ ...defaults, q: 'Indexed', sort: 'name-asc' });
+ expect(mockedApi.get.mock.calls.every(([url]) => url === '/documents')).toBe(true);
+ fireEvent.click(screen.getByRole('button', { name: 'History Back' }));
+ await waitFor(() => {
+ const restored = new URLSearchParams(location().search);
+ expect(restored.get('q')).toBeNull(); expect(restored.get('sort')).toBeNull();
+ expect(reads().at(-1)?.[1]?.params).toEqual(defaults);
+ });
+ expect(await screen.findByText('Initial metadata')).toBeVisible();
+ fireEvent.change(screen.getByRole('textbox', { name: copy('documentsSearch') }), { target: { value: 'After back' } });
+ await waitFor(() => expect(reads().at(-1)?.[1]?.params).toEqual({ ...defaults, q: 'After back' }));
+ const afterBack = location(); const afterBackQuery = new URLSearchParams(afterBack.search);
+ expect(afterBackQuery.get('q')).toBe('After back'); expect(afterBackQuery.get('sort')).toBeNull();
+ expect(afterBackQuery.get('keep')).toBe('æ&x'); expect(afterBack.hash).toBe('#context'); expect(afterBack.state).toEqual(state);
+ expect(screen.getByRole('textbox', { name: copy('documentsSearch') })).toHaveValue('After back');
+});
+
+test('[P7-07 documents] incompatible pending and failed queries hide old actions and late results cannot replace current metadata', async () => {
+ const old = deferred();
+ mockedApi.get.mockImplementation((_url: string, config?: any) => config.params.q === 'old' ? old.promise
+ : config.params.q === 'failed' ? Promise.reject(new Error('offline'))
+ : Promise.resolve({ data: page([item({ name: config.params.q === 'new' ? 'New owned metadata' : 'Initial metadata' })]) }) as never);
+ renderPage(); await screen.findByText('Initial metadata');
+ const search = screen.getByRole('textbox', { name: copy('documentsSearch') });
+ fireEvent.change(search, { target: { value: 'old' } });
+ await waitFor(() => expect((reads().at(-1)?.[1]?.params as Record | undefined)?.q).toBe('old'));
+ expect(screen.queryByText('Initial metadata')).not.toBeInTheDocument();
+ expect(screen.queryByRole('link', { name: copy('documentsOpenSource') })).not.toBeInTheDocument();
+ fireEvent.change(search, { target: { value: 'new' } });
+ expect(await screen.findByText('New owned metadata')).toBeVisible();
+ await act(async () => old.resolve({ data: page([item({ name: 'Obsolete private metadata' })]) }));
+ expect(screen.queryByText('Obsolete private metadata')).not.toBeInTheDocument();
+ fireEvent.change(search, { target: { value: 'failed' } });
+ expect(await screen.findByRole('alert')).toHaveTextContent(copy('documentsLoadFailed'));
+ expect(screen.queryByText('New owned metadata')).not.toBeInTheDocument();
+ expect(screen.queryByRole('link', { name: copy('documentsOpenSource') })).not.toBeInTheDocument();
+});
+
+test('[P7-07 documents] Retry is one guarded GET and a completion after leaving cannot publish or start follow-up work', async () => {
+ const pending = deferred();
+ mockedApi.get.mockRejectedValueOnce(new Error('offline')).mockImplementation(() => pending.promise);
+ renderPage('/documents?q=owned&type=attachment');
+ expect(await screen.findByRole('alert')).toHaveTextContent(copy('documentsLoadFailed'));
+ const retry = screen.getByRole('button', { name: copy('retry') });
+ act(() => { fireEvent.click(retry); fireEvent.click(retry); });
+ await waitFor(() => expect(reads()).toHaveLength(2));
+ expect(reads()[1][1]?.params).toEqual({ ...defaults, q: 'owned', type: 'attachment' });
+ fireEvent.click(screen.getByRole('button', { name: 'Leave documents' }));
+ await screen.findByText('Other workflow');
+ await act(async () => pending.resolve({ data: page([item({ name: 'Late document result' })]) }));
+ expect(screen.queryByText('Late document result')).not.toBeInTheDocument();
+ expect(reads()).toHaveLength(2); expect(URL.createObjectURL).not.toHaveBeenCalled();
+});
+
+test('[P7-07 documents] a true empty index, filtered no-match and request failure remain distinct', async () => {
+ mockedApi.get.mockImplementation((_url: string, config?: any) => config.params.q === 'offline'
+ ? Promise.reject(new Error('offline')) : Promise.resolve({ data: page([]) }) as never);
+ renderPage();
+ expect(await screen.findByText(copy('documentsEmpty'))).toBeVisible();
+ const search = screen.getByRole('textbox', { name: copy('documentsSearch') });
+ fireEvent.change(search, { target: { value: 'no match æøå' } });
+ expect(await screen.findByText(copy('documentsNoMatches'))).toBeVisible();
+ expect(screen.queryByText(copy('documentsEmpty'))).not.toBeInTheDocument();
+ fireEvent.change(search, { target: { value: 'offline' } });
+ expect(await screen.findByRole('alert')).toHaveTextContent(copy('documentsLoadFailed'));
+ expect(screen.queryByText(copy('documentsEmpty'))).not.toBeInTheDocument();
+ expect(screen.queryByText(copy('documentsNoMatches'))).not.toBeInTheDocument();
+});
+
+test('[P7-07 documents] English and Bokmål presentation changes preserve query context without a metadata refetch', async () => {
+ renderPage('/documents?q=%C3%A6%C3%B8%C3%A5&state=current&visibility=public&keep=one#context', { draft: 'unsaved-source-context' });
+ await screen.findByText('Metadata CV ÆØÅ'); const before = location(); const count = reads().length;
+ expect(screen.getByRole('textbox', { name: copy('documentsSearch') })).toHaveValue('æøå');
+ await language('nb');
+ expect(screen.getByRole('textbox', { name: copy('documentsSearch', {}, 'nb') })).toHaveValue('æøå');
+ expect(row('Metadata CV ÆØÅ').getByText(copy('documentsCurrent', {}, 'nb'))).toBeVisible();
+ expect(row('Metadata CV ÆØÅ').getByRole('link', { name: copy('documentsOpenSource', {}, 'nb') })).toHaveAttribute('href', '/career/builder/12');
+ await language('en');
+ expect(screen.getByRole('textbox', { name: copy('documentsSearch', {}, 'en') })).toHaveValue('æøå');
+ expect(location()).toEqual(before); expect(reads()).toHaveLength(count);
+});
+
+test('[P7-07 documents] an out-of-range page offers explicit first-page recovery without claiming an empty index', async () => {
+ mockedApi.get.mockImplementation((_url: string, config?: any) => Promise.resolve({ data: config.params.page === 999 ? page([], 999, 1) : page([item()]) } as never));
+ const state = { from: '/jobs/42', draftContext: 'preserve-out-of-range-context' };
+ renderPage('/documents?page=999&keep=%C3%A6%26x#materials', state);
+ expect(await screen.findByText(copy('documentsPageEmpty'))).toBeVisible();
+ expect(screen.getByText(copy('documentsPageEmptyHelp'))).toBeVisible();
+ expect(screen.queryByText(copy('documentsEmpty'))).not.toBeInTheDocument();
+ expect(screen.queryByText(copy('documentsNoMatches'))).not.toBeInTheDocument();
+ expect(reads()).toHaveLength(1);
+ expect(reads()[0][1]?.params).toEqual({ ...defaults, page: 999 });
+ fireEvent.click(screen.getByRole('button', { name: copy('documentsFirstPage') }));
+ expect(await screen.findByText('Metadata CV ÆØÅ')).toBeVisible();
+ expect(mockedApi.get.mock.calls.map(([url]) => url)).toEqual(['/documents', '/documents']);
+ expect(reads()[1][1]?.params).toEqual(defaults);
+ const current = location(); const query = new URLSearchParams(current.search);
+ expect(query.get('page')).not.toBe('999'); expect(query.get('keep')).toBe('æ&x');
+ expect(current.hash).toBe('#materials'); expect(current.state).toEqual(state);
+});
+
+test('[P7-07 documents] seven resource kinds retain exact source actions, frozen provenance and honest date labels', async () => {
+ const letter = item({ id: 'cover-letter:42', resourceId: '42', type: 'cover-letter', name: 'Versions-only letter', state: 'draft', visibility: null, dateKind: 'history-recorded-utc', version: 3, historyCount: 3, sourceHref: '/jobs/42?section=application&sub=letter', publicHref: null, hasCurrentContent: false });
+ const material = item({ id: 'application-material:42', resourceId: '42', type: 'application-material', name: 'Application answers', state: 'draft', visibility: null, recordedAt: null, dateKind: 'unknown', version: null, sourceHref: '/jobs/42?section=application&sub=answers', publicHref: null });
+ const tailored = item({ id: 'tailored-cv:42', resourceId: '42', type: 'tailored-cv', name: 'Tailored CV text', state: 'draft', visibility: null, recordedAt: null, dateKind: 'unknown', version: null, sourceHref: '/jobs/42?section=application&sub=cv', publicHref: null });
+ const upload = item({ id: 'attachment:9', resourceId: '9', type: 'attachment', name: 'Certificate æøå.pdf', visibility: null, recordedAt: '2026-02-12T09:15:00.0000000', dateKind: 'uploaded-local', version: null, fileType: 'application/pdf', fileSize: 1234, purpose: 'Certificate', useForAi: false, sourceHref: '/jobs/42?section=documents', downloadHref: '/api/attachments/download/9', publicHref: null });
+ const frozen = item({ id: 'submitted-package:86', resourceId: '86', type: 'submitted-package', name: 'Captured package', state: 'frozen', visibility: null, dateKind: 'captured-utc', version: 9, attachmentCount: 2, cvVariantId: 3, cvVariantName: 'Captured CV', cvVariantVersion: 2, jobIsDeleted: true, sourceHref: '/jobs/42?section=application&sub=submission', publicHref: null });
+ const email = item({ id: 'email-draft:11111111-1111-4111-8111-111111111111', resourceId: '11111111-1111-4111-8111-111111111111', type: 'email-draft', name: 'Saved email draft', state: 'draft', visibility: null, provider: 'gmail', version: 6, sourceHref: '/correspondence', publicHref: null });
+ mockedApi.get.mockResolvedValue({ data: page([item(), letter, material, tailored, upload, frozen, email]) } as never);
+ renderPage(); await screen.findByText('Metadata CV ÆØÅ');
+ for (const entry of [item(), letter, material, tailored, upload]) expect(row(entry.name).getByRole('link', { name: copy('documentsOpenSource') })).toHaveAttribute('href', entry.sourceHref);
+ expect(row('Metadata CV ÆØÅ').getByRole('link', { name: copy('documentsOpenPublicCv') })).toHaveAttribute('href', '/cv/metadata-cv');
+ expect(row(letter.name).getByText(copy('documentsHistoryOnly'))).toBeVisible();
+ expect(row(letter.name).getByText(copy('documentsHistoryVersions', { count: 3 }))).toBeVisible();
+ expect(row(upload.name).getByText(copy('documentsUploadedLocal'), { exact: false })).toBeVisible();
+ expect(row(material.name).getByText(copy('documentsDateUnknown'))).toBeVisible();
+ expect(row(tailored.name).getByText(copy('documentsDateUnknown'))).toBeVisible();
+ expect(row(frozen.name).getByText(copy('documentsFrozen'))).toBeVisible();
+ expect(row(frozen.name).getByText(copy('documentsCapturedCv', { name: 'Captured CV', version: 2 }))).toBeVisible();
+ expect(row(frozen.name).getByText(copy('documentsDeletedJob'))).toBeVisible();
+ expect(row(frozen.name).getByRole('link', { name: copy('documentsOpenSubmissionHistory') })).toHaveAttribute('href', frozen.sourceHref);
+ expect(row(email.name).getByRole('link', { name: copy('documentsOpenCorrespondence') })).toHaveAttribute('href', '/correspondence');
+ expect(row(email.name).queryByRole('button', { name: /resume|send/i })).not.toBeInTheDocument();
+ expect(screen.getByText(copy('documentsDateOrderHelp'))).toBeVisible();
+ expect(mockedApi.get.mock.calls.map(([url]) => url)).toEqual(['/documents']);
+ expect(URL.createObjectURL).not.toHaveBeenCalled();
+});
+
+test('[P7-07 documents] attachment download is explicit, API-relative and single-flight with original MIME, filename and URL disposal', async () => {
+ const upload = item({ id: 'attachment:9', resourceId: '9', type: 'attachment', name: 'Certificate æøå.pdf', visibility: null, fileType: 'application/pdf', sourceHref: '/jobs/42?section=documents', downloadHref: '/api/attachments/download/9', publicHref: null });
+ const pending = deferred(); const bytes = new Blob(['%PDF uploaded bytes'], { type: 'application/pdf' }); const clicks: Array<{ href: string; filename: string }> = [];
+ jest.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(function (this: HTMLAnchorElement) { clicks.push({ href: this.href, filename: this.download }); });
+ mockedApi.get.mockImplementation((url: string) => url === '/documents' ? Promise.resolve({ data: page([upload]) }) : url === '/attachments/download/9' ? pending.promise : Promise.reject(new Error(`Unexpected read ${url}`)) as never);
+ renderPage(); await screen.findByText(upload.name);
+ expect(mockedApi.get.mock.calls.map(([url]) => url)).toEqual(['/documents']);
+ const download = row(upload.name).getByRole('button', { name: copy('documentsDownloadAttachment') });
+ act(() => { fireEvent.click(download); fireEvent.click(download); });
+ expect(mockedApi.get.mock.calls.filter(([url]) => url === '/attachments/download/9')).toHaveLength(1);
+ expect(mockedApi.get).toHaveBeenLastCalledWith('/attachments/download/9', expect.objectContaining({ responseType: 'blob' }));
+ await act(async () => pending.resolve({ data: bytes, headers: { 'content-type': 'application/pdf' } }));
+ await waitFor(() => expect(clicks).toEqual([{ href: 'blob:uploaded-file', filename: upload.name }]));
+ const downloaded = (URL.createObjectURL as jest.Mock).mock.calls[0][0] as Blob;
+ expect(downloaded.type).toBe('application/pdf'); expect(downloaded.size).toBe(bytes.size);
+ expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:uploaded-file'); expect(reads()).toHaveLength(1);
+ await waitFor(() => expect(download).toHaveFocus());
+});
+
+test('[P7-07 documents] a completed download preserves deliberate Search focus instead of restoring the old button', async () => {
+ const upload = item({ id: 'attachment:9', resourceId: '9', type: 'attachment', name: 'Focus-safe uploaded file.pdf', visibility: null, fileType: 'application/pdf', sourceHref: '/jobs/42?section=documents', downloadHref: '/api/attachments/download/9', publicHref: null });
+ const pending = deferred(); const bytes = new Blob(['%PDF uploaded bytes'], { type: 'application/pdf' });
+ const click = jest.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => {});
+ mockedApi.get.mockImplementation((url: string) => url === '/documents' ? Promise.resolve({ data: page([upload]) }) : url === '/attachments/download/9' ? pending.promise : Promise.reject(new Error(`Unexpected read ${url}`)) as never);
+ renderPage(); await screen.findByText(upload.name);
+ const download = row(upload.name).getByRole('button', { name: copy('documentsDownloadAttachment') });
+ fireEvent.click(download);
+ expect(screen.getByRole('region', { name: copy('documentsYourDocuments') })).toHaveFocus();
+ const search = screen.getByRole('textbox', { name: copy('documentsSearch') });
+ act(() => search.focus()); expect(search).toHaveFocus();
+ await act(async () => pending.resolve({ data: bytes, headers: { 'content-type': 'application/pdf' } }));
+ await waitFor(() => expect(download).toBeEnabled());
+ expect(click).toHaveBeenCalledTimes(1);
+ expect(URL.createObjectURL).toHaveBeenCalledWith(bytes);
+ expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:uploaded-file');
+ expect(search).toHaveFocus();
+ expect(mockedApi.get.mock.calls.map(([url]) => url)).toEqual(['/documents', '/attachments/download/9']);
+});
+
+test('[P7-07 documents] download errors retry only that file and late download completion after leaving creates no URL', async () => {
+ const upload = item({ id: 'attachment:9', resourceId: '9', type: 'attachment', name: 'Private uploaded file.pdf', visibility: null, fileType: 'application/pdf', sourceHref: '/jobs/42?section=documents', downloadHref: '/api/attachments/download/9', publicHref: null });
+ const pending = deferred(); let attempts = 0;
+ mockedApi.get.mockImplementation((url: string) => url === '/documents' ? Promise.resolve({ data: page([upload]) }) : url === '/attachments/download/9'
+ ? ++attempts === 1 ? Promise.reject({ response: { status: 403 } }) : pending.promise : Promise.reject(new Error(`Unexpected read ${url}`)) as never);
+ renderPage(); await screen.findByText(upload.name);
+ fireEvent.click(row(upload.name).getByRole('button', { name: copy('documentsDownloadAttachment') }));
+ expect(await row(upload.name).findByRole('alert')).toHaveTextContent(copy('documentsDownloadFailed'));
+ await language('nb');
+ const error = row(upload.name).getByRole('alert'); expect(error).toHaveTextContent(copy('documentsDownloadFailed', {}, 'nb'));
+ const retry = within(error).getByRole('button', { name: copy('retry', {}, 'nb') });
+ act(() => { fireEvent.click(retry); fireEvent.click(retry); });
+ expect(attempts).toBe(2); expect(reads()).toHaveLength(1);
+ fireEvent.click(screen.getByRole('button', { name: 'Leave documents' })); await screen.findByText('Other workflow');
+ await act(async () => pending.resolve({ data: new Blob(['private file'], { type: 'application/pdf' }) }));
+ expect(URL.createObjectURL).not.toHaveBeenCalled(); expect(URL.revokeObjectURL).not.toHaveBeenCalled(); expect(reads()).toHaveLength(1);
+});
+
+test('[P7-07 documents] a published historical CV retains its percent-encoded Nordic public slug without fetching its body', async () => {
+ const historical = item({ name: 'Historical published CV', publicHref: '/cv/legacy-%C3%A6%C3%B8%C3%A5%20cv' });
+ mockedApi.get.mockResolvedValue({ data: page([historical]) } as never);
+ renderPage(); await screen.findByText(historical.name);
+ expect(row(historical.name).getByRole('link', { name: copy('documentsOpenPublicCv') })).toHaveAttribute('href', historical.publicHref);
+ expect(row(historical.name).getByRole('link', { name: copy('documentsOpenSource') })).toHaveAttribute('href', '/career/builder/12');
+ expect(mockedApi.get.mock.calls.map(([url]) => url)).toEqual(['/documents']);
+ expect(URL.createObjectURL).not.toHaveBeenCalled();
+});
+
+test('[P7-07 documents] malformed external and mismatched resource paths never become source, public or download actions', async () => {
+ const invalidCv = item({ name: 'Unsafe external CV', sourceHref: 'https://external.test/career/builder/12', publicHref: 'javascript:alert(1)' });
+ const wrongId = item({ id: 'cv:13', resourceId: '13', name: 'Mismatched CV source', sourceHref: '/career/builder/999', publicHref: '//external.test/cv/other' });
+ const invalidUpload = item({ id: 'attachment:9', resourceId: '9', type: 'attachment', name: 'Mismatched file', visibility: null, sourceHref: '/jobs/99?section=documents', downloadHref: '/api/attachments/download/999', publicHref: null });
+ mockedApi.get.mockResolvedValue({ data: page([invalidCv, wrongId, invalidUpload]) } as never);
+ renderPage(); await screen.findByText(invalidCv.name);
+ for (const entry of [invalidCv, wrongId, invalidUpload]) {
+ expect(row(entry.name).queryByRole('link', { name: copy('documentsOpenSource') })).not.toBeInTheDocument();
+ expect(row(entry.name).queryByRole('link', { name: copy('documentsOpenPublicCv') })).not.toBeInTheDocument();
+ expect(row(entry.name).queryByRole('button', { name: copy('documentsDownloadAttachment') })).not.toBeInTheDocument();
+ }
+ expect(mockedApi.get.mock.calls.map(([url]) => url)).toEqual(['/documents']);
+ expect(URL.createObjectURL).not.toHaveBeenCalled();
+});
diff --git a/job-tracker-ui/src/i18n/translations.ts b/job-tracker-ui/src/i18n/translations.ts
index 44afd24..bcc2dfd 100644
--- a/job-tracker-ui/src/i18n/translations.ts
+++ b/job-tracker-ui/src/i18n/translations.ts
@@ -21,6 +21,88 @@ export const translations = {
skipToMainContent: "Skip to main content",
jobSearch: "Job search",
career: "Career",
+ documentsTitle: "Documents",
+ goToDocuments: "Go to documents",
+ documentsPageSubtitle: "Find existing documents across your applications.",
+ documentsYourDocuments: "Your documents",
+ documentsIndexHelp: "Open the original workspace to edit a CV, draft or file. Recorded application snapshots keep their captured contents.",
+ documentsSearch: "Search documents",
+ documentsType: "Type",
+ documentsState: "State",
+ documentsVisibility: "CV visibility",
+ documentsJobFilter: "Linked job",
+ documentsJobFilterHelp: "Search your jobs to narrow the list. Existing links to jobs in trash are retained.",
+ documentsJobNumber: "Job {id}",
+ documentsNoJobs: "No matching jobs",
+ documentsJobsLoadFailed: "Could not load job choices.",
+ documentsOpenJobChoices: "Choose a linked job",
+ documentsCloseJobChoices: "Close job choices",
+ documentsClearJob: "Clear linked job filter",
+ documentsSort: "Sort",
+ documentsAllTypes: "All types",
+ documentsAllStates: "All states",
+ documentsAllVisibility: "All visibility",
+ documentsTypeCv: "CV",
+ documentsTypeLetter: "Cover letter",
+ documentsTypeMaterial: "Application answers and recruiter draft",
+ documentsTypeTailoredCv: "Tailored CV draft",
+ documentsTypeAttachment: "Uploaded file",
+ documentsTypePackage: "Application snapshot",
+ documentsTypeEmailDraft: "Email draft",
+ documentsCurrent: "Current",
+ documentsDraft: "Draft",
+ documentsFrozen: "Frozen",
+ documentsPrivate: "Private",
+ documentsPublic: "Public",
+ documentsSortDateDesc: "Recorded date: newest first",
+ documentsSortDateAsc: "Recorded date: oldest first",
+ documentsSortNameAsc: "Name: A–Z",
+ documentsSortNameDesc: "Name: Z–A",
+ documentsName: "Document",
+ documentsRecordedDate: "Recorded date",
+ documentsActions: "Actions",
+ documentsUntitled: "Untitled document",
+ documentsVersion: "Version {version}",
+ documentsHistoryVersions: "Saved versions: {count}",
+ documentsHistoryOnly: "Saved versions only; the current letter is empty.",
+ documentsAttachmentCount: "Archived files: {count}",
+ documentsCapturedCv: "Captured CV: {name}, version {version}",
+ documentsDeletedJob: "Job in trash",
+ documentsUnlinked: "Not linked to a job",
+ documentsUpdated: "Updated",
+ documentsCaptured: "Snapshot recorded",
+ documentsHistoryRecorded: "Latest saved version",
+ documentsUploadedLocal: "Uploaded; time zone not recorded",
+ documentsDateUnknown: "Date not recorded",
+ documentsDateOrderHelp: "Dates without a recorded time zone and missing dates appear last in either date order.",
+ documentsFrozenHelp: "A frozen snapshot records application material; it does not confirm delivery to an employer.",
+ documentsPublicHelp: "Public visibility does not record an application submission.",
+ documentsEmailHelp: "A saved draft does not confirm that an email was sent. Open Correspondence to find and review the draft.",
+ documentsCertificateHelp: "An uploaded certificate is not a verified Career Profile qualification.",
+ documentsFileSize: "{size} KB",
+ documentsOpenSource: "Open source",
+ documentsOpenCorrespondence: "Open correspondence",
+ documentsOpenSubmissionHistory: "Open submission history",
+ documentsOpenPublicCv: "View published CV",
+ documentsDownloadAttachment: "Download attachment",
+ documentsSourceUnavailable: "The original workspace is unavailable. Refresh the list to check this entry.",
+ documentsLoadFailed: "Could not load documents.",
+ documentsDownloadFailed: "Could not download this file.",
+ documentsDownloadUnavailable: "This file is no longer available. Refresh the document list.",
+ documentsRefresh: "Refresh",
+ documentsEmpty: "No documents yet",
+ documentsEmptyHelp: "Saved CVs, application drafts, uploaded files and application snapshots appear here. Create or edit them in their existing workspaces.",
+ documentsNoMatches: "No matching documents",
+ documentsNoMatchesHelp: "Try another search or change the filters.",
+ documentsPageEmpty: "No documents on this page",
+ documentsPageEmptyHelp: "The list has changed. Open the first page to see available documents.",
+ documentsResultCount: "Showing {shown} of {total} documents",
+ documentsPages: "Document pages",
+ documentsGoToPage: "Go to page {page}",
+ documentsPreviousPage: "Previous page",
+ documentsNextPage: "Next page",
+ documentsFirstPage: "First page",
+ documentsLastPage: "Last page",
applicationVersion: "Application version {version}",
expandSidebar: "Expand sidebar",
collapseSidebar: "Collapse sidebar",
@@ -2681,6 +2763,88 @@ export const translations = {
skipToMainContent: "Hopp til hovedinnhold",
jobSearch: "Jobbsøk",
career: "Karriere",
+ documentsTitle: "Dokumenter",
+ goToDocuments: "Gå til dokumenter",
+ documentsPageSubtitle: "Finn eksisterende dokumenter på tvers av søknadene dine.",
+ documentsYourDocuments: "Dine dokumenter",
+ documentsIndexHelp: "Åpne det opprinnelige arbeidsområdet for å redigere en CV, en kladd eller en fil. Lagrede øyeblikksbilder av søknader beholder innholdet som ble lagret.",
+ documentsSearch: "Søk i dokumenter",
+ documentsType: "Type",
+ documentsState: "Tilstand",
+ documentsVisibility: "CV-synlighet",
+ documentsJobFilter: "Tilknyttet stilling",
+ documentsJobFilterHelp: "Søk blant stillingene dine for å avgrense listen. Eksisterende koblinger til stillinger i papirkurven beholdes.",
+ documentsJobNumber: "Stilling {id}",
+ documentsNoJobs: "Ingen samsvarende stillinger",
+ documentsJobsLoadFailed: "Kunne ikke laste stillingsvalg.",
+ documentsOpenJobChoices: "Velg en tilknyttet stilling",
+ documentsCloseJobChoices: "Lukk stillingsvalg",
+ documentsClearJob: "Fjern stillingsfilter",
+ documentsSort: "Sortering",
+ documentsAllTypes: "Alle typer",
+ documentsAllStates: "Alle tilstander",
+ documentsAllVisibility: "All synlighet",
+ documentsTypeCv: "CV",
+ documentsTypeLetter: "Søknadsbrev",
+ documentsTypeMaterial: "Søknadssvar og kladd til rekrutterer",
+ documentsTypeTailoredCv: "Kladd til skreddersydd CV",
+ documentsTypeAttachment: "Opplastet fil",
+ documentsTypePackage: "Øyeblikksbilde av søknad",
+ documentsTypeEmailDraft: "E-postkladd",
+ documentsCurrent: "Gjeldende",
+ documentsDraft: "Kladd",
+ documentsFrozen: "Låst",
+ documentsPrivate: "Privat",
+ documentsPublic: "Offentlig",
+ documentsSortDateDesc: "Registrert dato: nyeste først",
+ documentsSortDateAsc: "Registrert dato: eldste først",
+ documentsSortNameAsc: "Navn: A–Å",
+ documentsSortNameDesc: "Navn: Å–A",
+ documentsName: "Dokument",
+ documentsRecordedDate: "Registrert dato",
+ documentsActions: "Handlinger",
+ documentsUntitled: "Dokument uten tittel",
+ documentsVersion: "Versjon {version}",
+ documentsHistoryVersions: "Lagrede versjoner: {count}",
+ documentsHistoryOnly: "Kun lagrede versjoner; gjeldende søknadsbrev er tomt.",
+ documentsAttachmentCount: "Arkiverte filer: {count}",
+ documentsCapturedCv: "Lagret CV: {name}, versjon {version}",
+ documentsDeletedJob: "Stillingen er i papirkurven",
+ documentsUnlinked: "Ikke knyttet til en stilling",
+ documentsUpdated: "Oppdatert",
+ documentsCaptured: "Øyeblikksbilde lagret",
+ documentsHistoryRecorded: "Siste lagrede versjon",
+ documentsUploadedLocal: "Lastet opp; tidssone er ikke registrert",
+ documentsDateUnknown: "Dato er ikke registrert",
+ documentsDateOrderHelp: "Datoer uten registrert tidssone og manglende datoer vises sist i begge datosorteringer.",
+ documentsFrozenHelp: "Et låst øyeblikksbilde lagrer søknadsmateriale; det bekrefter ikke levering til en arbeidsgiver.",
+ documentsPublicHelp: "Offentlig synlighet registrerer ikke en innsendt søknad.",
+ documentsEmailHelp: "En lagret kladd bekrefter ikke at en e-post er sendt. Åpne korrespondanse for å finne og gjennomgå kladden.",
+ documentsCertificateHelp: "Et opplastet sertifikat er ikke en bekreftet kvalifikasjon i karriereprofilen.",
+ documentsFileSize: "{size} KB",
+ documentsOpenSource: "Åpne opprinnelig arbeidsområde",
+ documentsOpenCorrespondence: "Åpne korrespondanse",
+ documentsOpenSubmissionHistory: "Åpne innsendingshistorikk",
+ documentsOpenPublicCv: "Se publisert CV",
+ documentsDownloadAttachment: "Last ned vedlegg",
+ documentsSourceUnavailable: "Det opprinnelige arbeidsområdet er utilgjengelig. Oppdater listen for å kontrollere oppføringen.",
+ documentsLoadFailed: "Kunne ikke laste dokumenter.",
+ documentsDownloadFailed: "Kunne ikke laste ned denne filen.",
+ documentsDownloadUnavailable: "Denne filen er ikke lenger tilgjengelig. Oppdater dokumentlisten.",
+ documentsRefresh: "Oppdater",
+ documentsEmpty: "Ingen dokumenter ennå",
+ documentsEmptyHelp: "Lagrede CV-er, søknadskladder, opplastede filer og øyeblikksbilder av søknader vises her. Opprett eller rediger dem i de eksisterende arbeidsområdene.",
+ documentsNoMatches: "Ingen samsvarende dokumenter",
+ documentsNoMatchesHelp: "Prøv et annet søk eller endre filtrene.",
+ documentsPageEmpty: "Ingen dokumenter på denne siden",
+ documentsPageEmptyHelp: "Listen er endret. Åpne den første siden for å se tilgjengelige dokumenter.",
+ documentsResultCount: "Viser {shown} av {total} dokumenter",
+ documentsPages: "Dokumentsider",
+ documentsGoToPage: "Gå til side {page}",
+ documentsPreviousPage: "Forrige side",
+ documentsNextPage: "Neste side",
+ documentsFirstPage: "Første side",
+ documentsLastPage: "Siste side",
applicationVersion: "Applikasjonsversjon {version}",
expandSidebar: "Utvid sidemenyen",
collapseSidebar: "Skjul sidemenyen",
diff --git a/job-tracker-ui/src/quick-command-preservation.test.tsx b/job-tracker-ui/src/quick-command-preservation.test.tsx
index 627b6e2..f9a6fa3 100644
--- a/job-tracker-ui/src/quick-command-preservation.test.tsx
+++ b/job-tracker-ui/src/quick-command-preservation.test.tsx
@@ -3,6 +3,7 @@ import "@testing-library/jest-dom";
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { api } from "./api";
import { I18nProvider } from "./i18n/I18nProvider";
+import { translations } from "./i18n/translations";
import QuickCommandDialog from "./components/QuickCommandDialog";
const mockedApi = api as jest.Mocked;
@@ -48,7 +49,7 @@ test("[PRES-E07 PRES-J04] the capture command opens review without creating a jo
test("[P5-00 PRES-E07] command and empty results use valid list semantics", async () => {
render();
- expect(screen.getAllByRole("listitem")).toHaveLength(6);
+ expect(screen.getAllByRole("listitem")).toHaveLength(7);
expect(screen.getAllByRole("list").every(list => Array.from(list.children).every(item => item.tagName === "LI"))).toBe(true);
fireEvent.change(screen.getByRole("textbox"), { target: { value: "no-such-result" } });
mockedApi.get.mockResolvedValue({ data: [] } as never);
@@ -125,3 +126,19 @@ test("[P5-02 PRES-E07] arrowing from a focused result keeps focus and activation
fireEvent.click(jobs);
expect(onNavigate).toHaveBeenCalledWith("/jobs");
});
+
+test("[P7-07 PRES-E07] the Documents command is localized and opens its owner index without fetching bodies", async () => {
+ const onNavigate = jest.fn();
+ const onClose = jest.fn();
+ render();
+ const english = (translations.en as Record).goToDocuments;
+ expect(screen.getByRole("button", { name: name => name.includes(english) })).toBeVisible();
+ await act(async () => { window.dispatchEvent(new StorageEvent("storage", { key: "uiLanguage", newValue: "nb" })); });
+ const bokmal = (translations.nb as Record).goToDocuments;
+ fireEvent.click(screen.getByRole("button", { name: name => name.includes(bokmal) }));
+ expect(onNavigate).toHaveBeenCalledTimes(1);
+ expect(onNavigate).toHaveBeenCalledWith("/documents");
+ expect(onClose).toHaveBeenCalledTimes(1);
+ expect(mockedApi.get).not.toHaveBeenCalled();
+ expect(mockedApi.post).not.toHaveBeenCalled();
+});
diff --git a/job-tracker-ui/src/views/DocumentsPage.tsx b/job-tracker-ui/src/views/DocumentsPage.tsx
new file mode 100644
index 0000000..4e054ad
--- /dev/null
+++ b/job-tracker-ui/src/views/DocumentsPage.tsx
@@ -0,0 +1,299 @@
+import React, { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
+import { Link as RouterLink, useLocation, useNavigate, useSearchParams } from "react-router-dom";
+import { Alert, Autocomplete, Box, Button, Chip, FormControl, InputLabel, MenuItem, Pagination, Paper, Select, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, useMediaQuery, useTheme } from "@mui/material";
+import RefreshIcon from "@mui/icons-material/Refresh";
+import { api } from "../api";
+import ViewStateNotice from "../components/ViewStateNotice";
+import type { ViewResourceError } from "../hooks/useViewResource";
+import { useI18n } from "../i18n/I18nProvider";
+import type { TranslationKey } from "../i18n/translations";
+import { PageHeader } from "../layout/PageLayout";
+
+type DocumentItem = {
+ id: string; resourceId: string; type: string; name: string; state: string; visibility: string | null;
+ jobApplicationId: number | null; jobTitle: string | null; companyName: string | null; jobIsDeleted: boolean | null;
+ recordedAt: string | null; dateKind: string; version: number | null; historyCount: number | null;
+ attachmentCount: number | null; cvVariantId: number | null; cvVariantName: string | null; cvVariantVersion: number | null;
+ fileType: string | null; fileSize: number | null; purpose: string | null; useForAi: boolean | null;
+ provider: string | null; sourceHref: string; downloadHref: string | null; publicHref: string | null;
+ hasCurrentContent: boolean | null;
+};
+type DocumentsResult = { items: DocumentItem[]; page: number; pageSize: number; total: number; totalPages: number };
+type JobChoice = { id: number; jobTitle: string; companyName: string };
+type DownloadState = { id: string; busy: boolean; status?: number };
+
+const types: Array<[string, TranslationKey]> = [["all", "documentsAllTypes"], ["cv", "documentsTypeCv"], ["cover-letter", "documentsTypeLetter"], ["application-material", "documentsTypeMaterial"], ["tailored-cv", "documentsTypeTailoredCv"], ["attachment", "documentsTypeAttachment"], ["submitted-package", "documentsTypePackage"], ["email-draft", "documentsTypeEmailDraft"]];
+const states: Array<[string, TranslationKey]> = [["all", "documentsAllStates"], ["current", "documentsCurrent"], ["draft", "documentsDraft"], ["frozen", "documentsFrozen"]];
+const visibilityOptions: Array<[string, TranslationKey]> = [["all", "documentsAllVisibility"], ["private", "documentsPrivate"], ["public", "documentsPublic"]];
+const sorts: Array<[string, TranslationKey]> = [["date-desc", "documentsSortDateDesc"], ["date-asc", "documentsSortDateAsc"], ["name-asc", "documentsSortNameAsc"], ["name-desc", "documentsSortNameDesc"]];
+const option = (value: string | null, choices: Array<[string, TranslationKey]>, fallback = "all") => choices.some(([key]) => key === value) ? value! : fallback;
+const positiveInteger = (value: string | null, maximum = Number.MAX_SAFE_INTEGER) => value && /^\d+$/.test(value) && Number.isSafeInteger(Number(value)) && Number(value) > 0 && Number(value) <= maximum ? Number(value) : undefined;
+const errorState = (error: any): ViewResourceError => ({ kind: [401, 403].includes(error?.response?.status) ? "unauthorized" : "error", status: error?.response?.status, message: "", retryable: ![401, 403].includes(error?.response?.status) });
+
+function sourcePath(item: DocumentItem) {
+ if (item.id !== `${item.type}:${item.resourceId}`) return null;
+ if (item.type === "email-draft") return /^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i.test(item.resourceId) && item.sourceHref === "/correspondence" ? item.sourceHref : null;
+ if (!/^[1-9]\d*$/.test(item.resourceId)) return null;
+ const job = item.jobApplicationId;
+ const jobPath = Number.isSafeInteger(job) && Number(job) > 0 ? `/jobs/${job}` : null;
+ const expected = item.type === "cv" ? `/career/builder/${item.resourceId}`
+ : item.type === "attachment" ? jobPath && `${jobPath}?section=documents`
+ : item.type === "cover-letter" ? jobPath && `${jobPath}?section=application&sub=letter`
+ : item.type === "application-material" ? jobPath && `${jobPath}?section=application&sub=answers`
+ : item.type === "tailored-cv" ? jobPath && `${jobPath}?section=application&sub=cv`
+ : item.type === "submitted-package" ? jobPath && `${jobPath}?section=application&sub=submission` : null;
+ return expected && item.sourceHref === expected ? expected : null;
+}
+
+function publicPath(item: DocumentItem) {
+ if (!sourcePath(item) || item.type !== "cv" || item.visibility !== "public" || !/^\/cv\/(?:[\w.~\-]|%[\da-f]{2})+$/i.test(item.publicHref ?? "")) return null;
+ try {
+ const slug = decodeURIComponent(item.publicHref!.slice(4));
+ return slug && slug !== "." && slug !== ".." ? item.publicHref : null;
+ } catch { return null; }
+}
+
+export default function DocumentsPage() {
+ const { language, t } = useI18n();
+ const location = useLocation();
+ const navigate = useNavigate();
+ const [params] = useSearchParams();
+ const theme = useTheme();
+ const desktop = useMediaQuery(theme.breakpoints.up("md"));
+ const labelId = useId();
+ const q = (params.get("q") ?? "").slice(0, 200);
+ const search = q.trim();
+ const type = option(params.get("type"), types);
+ const state = option(params.get("state"), states);
+ const visibility = option(params.get("visibility"), visibilityOptions);
+ const sort = option(params.get("sort"), sorts, "date-desc");
+ const page = positiveInteger(params.get("page"), 10000) ?? 1;
+ // React Compiler needs a primitive dependency after the shared ID validator.
+ const jobApplicationId = Number(positiveInteger(params.get("jobApplicationId"), 2147483647)) || undefined;
+ const key = JSON.stringify([search, type, state, visibility, sort, page, jobApplicationId]);
+ const currentKey = useRef(key);
+ currentKey.current = key;
+ const [result, setResult] = useState(null);
+ const [loadedKey, setLoadedKey] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const request = useRef(0);
+ const pendingKey = useRef(null);
+ const listController = useRef(null);
+ const mounted = useRef(true);
+ const resultsRef = useRef(null);
+ const [downloadState, setDownloadState] = useState(null);
+ const downloadController = useRef(null);
+ const downloadFocus = useRef<{ button: HTMLButtonElement; key: string } | null>(null);
+ const [jobsOpen, setJobsOpen] = useState(false);
+ const [jobQuery, setJobQuery] = useState("");
+ const [jobs, setJobs] = useState([]);
+ const [jobsKey, setJobsKey] = useState(null);
+ const [jobsLoading, setJobsLoading] = useState(false);
+ const [jobsError, setJobsError] = useState(null);
+ const jobsRequest = useRef(0);
+ const jobsPending = useRef(null);
+ const queryEdits = useRef({ location, params });
+
+ const updateQuery = (changes: Record, replace = false) => {
+ const next = new URLSearchParams(queryEdits.current.location === location ? queryEdits.current.params : params);
+ for (const [name, value] of Object.entries(changes)) {
+ if (!value || (value === "all" && ["type", "state", "visibility"].includes(name)) || (name === "page" && value === "1")) next.delete(name);
+ else next.set(name, value);
+ }
+ queryEdits.current = { location, params: next };
+ navigate({ pathname: location.pathname, search: next.size ? `?${next}` : "", hash: location.hash }, { state: location.state, replace });
+ };
+
+ const load = useCallback(async () => {
+ if (!mounted.current || pendingKey.current === key) return;
+ const sequence = ++request.current;
+ pendingKey.current = key;
+ listController.current?.abort();
+ const controller = new AbortController();
+ listController.current = controller;
+ setLoading(true);
+ setError(null);
+ try {
+ const response = await api.get("/documents", { params: { page, pageSize: 25, q: search || undefined, type, state, visibility, sort, ...(jobApplicationId ? { jobApplicationId } : {}) }, signal: controller.signal });
+ if (!mounted.current || sequence !== request.current || key !== currentKey.current) return;
+ setResult(response.data);
+ setLoadedKey(key);
+ } catch (failure) {
+ if (mounted.current && sequence === request.current && !controller.signal.aborted) setError(errorState(failure));
+ } finally {
+ if (mounted.current && sequence === request.current) { pendingKey.current = null; setLoading(false); }
+ }
+ }, [jobApplicationId, key, page, search, sort, state, type, visibility]);
+
+ useEffect(() => {
+ mounted.current = true;
+ return () => { mounted.current = false; request.current += 1; jobsRequest.current += 1; listController.current?.abort(); downloadController.current?.abort(); };
+ }, []);
+ useEffect(() => {
+ setDownloadState(null);
+ downloadController.current?.abort();
+ downloadController.current = null;
+ void load();
+ return () => { request.current += 1; pendingKey.current = null; listController.current?.abort(); };
+ }, [load]);
+ useEffect(() => {
+ const target = downloadFocus.current;
+ if (downloadState?.busy || !target) return;
+ downloadFocus.current = null;
+ if (document.activeElement === resultsRef.current && target.key === currentKey.current && target.button.isConnected && !target.button.disabled) target.button.focus();
+ }, [downloadState]);
+
+ const loadJobs = useCallback(async () => {
+ const search = jobQuery.trim();
+ if (!mounted.current || jobsPending.current === search) return;
+ const sequence = ++jobsRequest.current;
+ jobsPending.current = search;
+ setJobsLoading(true);
+ setJobsError(null);
+ try {
+ const response = await api.get("/jobapplications/choices", { params: { q: search || undefined, limit: 20 } });
+ if (mounted.current && sequence === jobsRequest.current) { setJobs(response.data); setJobsKey(search); }
+ } catch (failure) {
+ if (mounted.current && sequence === jobsRequest.current) setJobsError(errorState(failure));
+ } finally {
+ if (sequence === jobsRequest.current) { jobsPending.current = null; if (mounted.current) setJobsLoading(false); }
+ }
+ }, [jobQuery]);
+ useEffect(() => {
+ if (!jobsOpen) return;
+ const timer = window.setTimeout(() => void loadJobs(), jobQuery.trim() ? 250 : 0);
+ return () => { window.clearTimeout(timer); jobsRequest.current += 1; jobsPending.current = null; };
+ }, [jobQuery, jobsOpen, loadJobs]);
+
+ const refresh = () => { resultsRef.current?.focus(); setDownloadState(null); void load(); };
+ const download = async (item: DocumentItem, button?: HTMLButtonElement) => {
+ if (!sourcePath(item) || item.type !== "attachment" || item.downloadHref !== `/api/attachments/download/${item.resourceId}` || downloadController.current) return;
+ if (button) downloadFocus.current = { button, key };
+ resultsRef.current?.focus();
+ const capturedKey = key;
+ const controller = new AbortController();
+ downloadController.current = controller;
+ setDownloadState({ id: item.id, busy: true });
+ try {
+ const response = await api.get(`/attachments/download/${item.resourceId}`, { responseType: "blob", signal: controller.signal });
+ if (!mounted.current || controller.signal.aborted || capturedKey !== currentKey.current) return;
+ const blob = response.data instanceof Blob ? response.data : new Blob([response.data], { type: item.fileType ?? "application/octet-stream" });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ try {
+ link.href = url;
+ link.download = item.name;
+ document.body.appendChild(link);
+ link.click();
+ } finally { link.remove(); URL.revokeObjectURL(url); }
+ setDownloadState(null);
+ } catch (failure: any) {
+ if (mounted.current && !controller.signal.aborted && capturedKey === currentKey.current) setDownloadState({ id: item.id, busy: false, status: failure?.response?.status ?? 0 });
+ } finally {
+ if (downloadController.current === controller) downloadController.current = null;
+ }
+ };
+
+ const current = loadedKey === key && !loading && !error;
+ const items = current ? result?.items ?? [] : [];
+ const filtered = !!q.trim() || type !== "all" || state !== "all" || visibility !== "all" || !!jobApplicationId;
+ const locale = language === "nb" ? "nb-NO" : "en-GB";
+ const selectedJob = useMemo(() => {
+ if (!jobApplicationId) return null;
+ const choice = jobs.find((job) => job.id === jobApplicationId);
+ if (choice) return choice;
+ const item = result?.items.find((document) => document.jobApplicationId === jobApplicationId);
+ return { id: jobApplicationId, jobTitle: item?.jobTitle ?? t("documentsJobNumber", { id: jobApplicationId }), companyName: item?.companyName ?? "" };
+ }, [jobApplicationId, jobs, result, t]);
+ const from = { from: `${location.pathname}${location.search}${location.hash}`, fromState: location.state };
+ const buttonSx = { whiteSpace: "normal", overflowWrap: "anywhere", maxWidth: "100%", minHeight: 40 } as const;
+ const metadataSx = { overflowWrap: "anywhere", minWidth: 0 } as const;
+
+ const date = (item: DocumentItem) => {
+ const label: TranslationKey = item.dateKind === "uploaded-local" ? "documentsUploadedLocal" : item.dateKind === "captured-utc" ? "documentsCaptured" : item.dateKind === "history-recorded-utc" ? "documentsHistoryRecorded" : "documentsUpdated";
+ let value: Date | null = null;
+ if (item.recordedAt && item.dateKind === "uploaded-local") {
+ const local = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)/.exec(item.recordedAt);
+ // UTC is only a formatting carrier for the unzoned wall-clock components.
+ if (local) value = new Date(Date.UTC(+local[1], +local[2] - 1, +local[3], +local[4], +local[5], +local[6]));
+ } else if (item.recordedAt && ["updated-utc", "captured-utc", "history-recorded-utc"].includes(item.dateKind)) value = new Date(item.recordedAt);
+ if (!value || Number.isNaN(value.valueOf())) return {t("documentsDateUnknown")};
+ return {t(label)}{value.toLocaleString(locale, { dateStyle: "medium", timeStyle: "short", ...(item.dateKind === "uploaded-local" ? { timeZone: "UTC" } : {}) })};
+ };
+ const badges = (item: DocumentItem) =>
+ value === item.state)?.[1] ?? "documentsCurrent")} />
+ {item.type === "cv" && item.visibility ? : null}
+ {item.jobIsDeleted ? : null}
+ ;
+ const details = (item: DocumentItem) =>
+ {item.name || t("documentsUntitled")}
+ {t(types.find(([value]) => value === item.type)?.[1] ?? "documentsTypeAttachment")}{item.version != null ? {` · ${t("documentsVersion", { version: item.version })}`} : null}
+ {item.type === "cv" && item.visibility === "public" ? {t("documentsPublicHelp")} : null}
+ {item.type === "cover-letter" && item.hasCurrentContent === false ? {t("documentsHistoryOnly")} : null}
+ {item.historyCount != null && item.historyCount > 0 ? {t("documentsHistoryVersions", { count: item.historyCount })} : null}
+ {item.type === "submitted-package" ? <>
+ {item.cvVariantName && item.cvVariantVersion != null ? {t("documentsCapturedCv", { name: item.cvVariantName, version: item.cvVariantVersion })} : null}
+ {item.attachmentCount != null ? {t("documentsAttachmentCount", { count: item.attachmentCount })} : null}
+ {t("documentsFrozenHelp")}
+ > : null}
+ {item.type === "email-draft" ? {t("documentsEmailHelp")} : null}
+ {item.type === "attachment" ? <>
+ {[item.fileType, item.fileSize != null ? t("documentsFileSize", { size: new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }).format(item.fileSize / 1024) }) : null].filter(Boolean).join(" · ")}
+ {item.purpose ? {t("attachmentsPurpose")}: {t(({ resume: "attachmentsPurposeResume", "cover-letter": "attachmentsPurposeCoverLetter", portfolio: "attachmentsPurposePortfolio", "case-study": "attachmentsPurposeCaseStudy", certificate: "attachmentsPurposeCertificate" } as Record)[item.purpose.toLowerCase()] ?? "attachmentsPurposeOther")} : null}
+ {item.purpose?.toLowerCase() === "certificate" ? {t("documentsCertificateHelp")} : null}
+ {item.useForAi != null ? {t("attachmentsAiUse")}: {t(item.useForAi ? "attachmentsAiEnabled" : "attachmentsAiDisabled")} : null}
+ > : null}
+ ;
+ const job = (item: DocumentItem) => {item.jobTitle ? [item.jobTitle, item.companyName].filter(Boolean).join(" · ") : t("documentsUnlinked")};
+ const actions = (item: DocumentItem) => {
+ const source = sourcePath(item);
+ const ownDownload = source && item.type === "attachment" && item.downloadHref === `/api/attachments/download/${item.resourceId}`;
+ const publicLink = publicPath(item);
+ const failed = downloadState?.id === item.id && !downloadState.busy && downloadState.status !== undefined;
+ return
+
+ {source ? : {t("documentsSourceUnavailable")}}
+ {publicLink ? : null}
+ {ownDownload ? : null}
+
+ {failed ? downloadState?.status === 404 ? refresh() : void download(item, event.currentTarget)}>{t(downloadState?.status === 404 ? "documentsRefresh" : "retry")}}>{t(downloadState?.status === 404 ? "documentsDownloadUnavailable" : "documentsDownloadFailed")} : null}
+ ;
+ };
+ const filter = (name: string, title: TranslationKey, value: string, options: Array<[string, TranslationKey]>) =>
+ {t(title)}
+
+ ;
+
+ return
+ } onClick={refresh} disabled={loading} sx={buttonSx}>{t("documentsRefresh")}} />
+
+ updateQuery({ q: event.target.value, page: "1" }, true)} slotProps={{ htmlInput: { maxLength: 200 } }} />
+
+ {filter("type", "documentsType", type, types)}
+ {filter("state", "documentsState", state, states)}
+ {filter("visibility", "documentsVisibility", visibility, visibilityOptions)}
+ choices} isOptionEqualToValue={(a, b) => a.id === b.id} getOptionLabel={(choice) => [choice.jobTitle, choice.companyName].filter(Boolean).join(" · ")} onOpen={() => setJobsOpen(true)} onClose={() => setJobsOpen(false)} onInputChange={(_, value, reason) => { if (reason === "input") setJobQuery(value); else if (reason === "reset") setJobQuery(""); }} onChange={(_, choice) => updateQuery({ jobApplicationId: choice ? String(choice.id) : "", page: "1" })} noOptionsText={t(jobsError ? "documentsJobsLoadFailed" : "documentsNoJobs")} loadingText={t("loading")} openText={t("documentsOpenJobChoices")} closeText={t("documentsCloseJobChoices")} clearText={t("documentsClearJob")} renderInput={(input) => } />
+ {filter("sort", "documentsSort", sort, sorts)}
+
+ {jobsError ? { resultsRef.current?.focus(); return loadJobs(); }} /> : null}
+ {t("documentsDateOrderHelp")}
+
+
+
+ {current && result ? <>
+ {t("documentsResultCount", { shown: items.length, total: result.total })}
+ {items.length === 0 ?
+ {t(result.total > 0 ? "documentsPageEmpty" : filtered ? "documentsNoMatches" : "documentsEmpty")}
+ {t(result.total > 0 ? "documentsPageEmptyHelp" : filtered ? "documentsNoMatchesHelp" : "documentsEmptyHelp")}
+ {result.total > 0 ? : null}
+ : desktop ?
+
+ {t("documentsName")}{t("documentsJobFilter")}{t("documentsState")}{t("documentsRecordedDate")}{t("documentsActions")}
+ {items.map((item) => {details(item)}{job(item)}{badges(item)}{date(item)}{actions(item)})}
+
: {items.map((item) => {details(item)}{job(item)}{badges(item)}{date(item)}{actions(item)})}}
+ {result.totalPages > 1 ? { resultsRef.current?.focus(); updateQuery({ page: String(next) }); }} aria-label={t("documentsPages")} getItemAriaLabel={(kind, target) => t(kind === "page" ? "documentsGoToPage" : kind === "previous" ? "documentsPreviousPage" : kind === "next" ? "documentsNextPage" : kind === "first" ? "documentsFirstPage" : "documentsLastPage", { page: target ?? 1 })} sx={{ mt: 2, "& .MuiPagination-ul": { flexWrap: "wrap", gap: 0.5 } }} /> : null}
+ > : null}
+
+ ;
+}