diff --git a/docs/redesign/phase-7/COMMITS.md b/docs/redesign/phase-7/COMMITS.md index a0d2bfd..b93159e 100644 --- a/docs/redesign/phase-7/COMMITS.md +++ b/docs/redesign/phase-7/COMMITS.md @@ -2,6 +2,8 @@ ## Continuation — 20 October 2026 +P7-11 shared **d8c01873053e59c896e60911b8e5c14b00693c7f** normally pushed/directly verified18:26:54UTC, retaining all remaining owner WIP. [Generated-label localisation](p7-11/LOCALISATION.md) final boundary:76/4 direct plus7/2 compatibility consumer vectors; native9/9 PASS18:15:24–18:17:05UTC,50scans0violations/355incomplete observations, three actual images inspected. Final hygiene/publication follows; no provider/model/save contract changes. + P7-10 System **d824f59d2b3e9b680bee5952802aa3c0453aee03** normally pushed/directly verified clean13:40:25UTC. CI-only main repair merge **6db62095c6dc6f7e8275447827c62b09f9b2d2e3** pushed/directly verified clean13:40:41UTC. P7-10 is implemented CONDITIONAL GO and published. P7-11 [shared integration](p7-11/SHARED.md) has80/11 affected/consumer frontend, current7version/10command vectors and native5modal+1Strategy PASS; final hygiene/publication follows.59paired label additions are the shared foundation for subsequent responsible-owner commits. Whole candidate frontend898/101 and backend1057 pass; final ARIA continuations receive focused/native proof and fresh P7-12 regression. Independent fixture/focus repair **bba52af4b8724f1a5c74013ce1476a20ca3972c2** normally pushed/directly verified on development18:05:08UTC, with all P7-11 WIP retained. Main's equivalent **3e1057d** plus Qwen adapter cherry-pick **f7dc22e5e7efe1fd1d64f6ace2c04cf2f95bf558** are pushed/directly verified18:05UTC. Release833 tests; no verified deployment/model activation or production GO claim. Run832's16failures are explained in its release report; one flaky Rules case remains debt. diff --git a/docs/redesign/phase-7/PRESERVATION.md b/docs/redesign/phase-7/PRESERVATION.md index f74dbab..4410389 100644 --- a/docs/redesign/phase-7/PRESERVATION.md +++ b/docs/redesign/phase-7/PRESERVATION.md @@ -1,5 +1,7 @@ # Phase 7 — Initial preservation matrix +P7-11 [localisation](p7-11/LOCALISATION.md) adds bounded PRES-C08/C10/W04/M04/E09 evidence: original template selection objects/IDs/admission and English/custom metadata retained; known Bokmål generated labels and history enums translate without modifying user text. Resumed-draft fallback derives current language without rereads or dirty-field loss. Native9cases/50scans0violations retain355qualified incomplete observations. All57 groups remain56 PARTIAL/C11 DEFERRED and33 routes; no provider/model/revision/renderer or production acceptance claim. + P7-11 [shared integration](p7-11/SHARED.md) adds bounded PRES-E07/E08/W04/W09 evidence: valid badge/command names, retained item-specific checklist/interview actions, contextual form headings and Strategy GET-only error recovery with exact intentional-empty contract. Existing AI/Pro and operations semantics remain. All57 groups stay56 PARTIAL/C11 DEFERRED and33 routes; accessibility incompletes/manual/provider/output/production gates remain qualified. No Phase8 work. P7-10 [System](p7-10/SYSTEM.md) adds bounded PRES-A03/E09 proof: four independent diagnostic reads, explicit failed-read availability, denied-refresh disposal, GET-only retry, unchanged SMTP password/clear payloads, retained newer drafts and synchronized/session-guarded operations.20unit/46backend/3native cases pass; administrator browser writes are controlled fixtures, not operational acceptance. All57 groups remain56 PARTIAL/C11 DEFERRED and33 routes. No schema/renderer/provider/AI-safety change. Human/device/provider/output/release qualifications remain. diff --git a/docs/redesign/phase-7/p7-11/LOCALISATION.md b/docs/redesign/phase-7/p7-11/LOCALISATION.md new file mode 100644 index 0000000..0f4bd71 --- /dev/null +++ b/docs/redesign/phase-7/p7-11/LOCALISATION.md @@ -0,0 +1,15 @@ +# Generated-label localisation + +**Implemented CONDITIONAL GO; final hygiene/publication pending.** Existing gallery, Reminders, application materials and correspondence owners remain. + +Known built-in template IDs map to Bokmål names/categories/descriptions; English retains server metadata and unknown templates remain verbatim. Selection still passes the original theme object and preserves IDs, availability, Pro admission and renderer settings. A full frontend consumer caught an initial change to English descriptions; limiting the mapping to Bokmål restored the existing English contract, with7affected gallery/document-control cases passing. + +Only recognized generated reminder strings and anchored numeric reasons translate; custom reasons remain verbatim. Tailoring headings/details map known kinds, preserving evidence/items and unknown kinds. Known cover-letter source/action enums translate; unknown values and authored prose remain unchanged. Existing CV history receives the corresponding display mapping in its own error/navigation boundary. No saved data or generated content is rewritten. + +Resumed draft context stores absent metadata as absent and derives its fallback from the current locale at render time. Real company/job/source/subject and dirty content remain unchanged. Singular item/attachment labels use paired translations. Locale changes do not trigger a draft or setup reread. + +Initial direct result70passed/6failed; final direct result **76/76 across four suites PASS**, app TypeScript/targeted lint PASS. Existing modified assertions and new tests are reconciled in the final inventory, rather than counting every initial failure as a new case. Browser consumers initially still expected raw English generated reminders in Bokmål; those finite fixtures now assert their translated generated labels while retaining exact custom-content, no-refetch, route, focus and mutation checks. Final native/visual/static/publication evidence follows. + +Final native **9/9 PASS**, zero skips/flaky/retries,18:15:24–18:17:05UTC;52images/50all-enabled axe scans,0violations/355incomplete-node observations. [Portable results](evidence/localization-results.json) retain raw result/source/image identities. Three images were actually inspected:320px Bokmål gallery,1440px Bokmål generated reasons and1024px English dark retained draft. Others remain automated evidence. Incompletes include336contrast observations (short badges, backgrounds obscured by fixed chrome/focus/dialogs) and19hidden-focus vectors in MUI background/menu portal transitions; these retain manual/settled-state qualification, not a reader pass. + +No provider/model operation, canonical Career data, revision/save contract, frozen package or renderer geometry changes. Human Bokmål/editorial review and physical/assistive acceptance are not inferred from automation. Existing deferred functionality and production qualifications remain. Static checks and normal push follow. diff --git a/docs/redesign/phase-7/p7-11/README.md b/docs/redesign/phase-7/p7-11/README.md index 994cb2b..2bfdd54 100644 --- a/docs/redesign/phase-7/p7-11/README.md +++ b/docs/redesign/phase-7/p7-11/README.md @@ -20,7 +20,7 @@ These findings were carried from implemented checkpoints and verified in source, ## Execution boundaries 1. [Shared accessibility and forms](SHARED.md), including the paired translation foundation used by the following owners. -2. Generated-label localisation follows separately, preserving all stored metadata and user text. +2. [Generated-label localisation](LOCALISATION.md), preserving all stored metadata and user text; final hygiene/publication follows. 3. Documents and error navigation follows separately, including CV history display in its existing owner. The independently authorized release repair is separate: development `bba52af4b8724f1a5c74013ce1476a20ca3972c2` was pushed/directly verified18:05:08UTC. Main contains equivalent fixture/focus repair `3e1057d` and Qwen compatibility `f7dc22e`, normally pushed and directly verified20October18:05UTC. Run833 is testing; deployment/model activation are unverified. [Release evidence](../../../infrastructure/2026-10-20-browser-fixture-and-focus-contracts.md) retains run832's314passing/16failed/oneflaky outcome and the two demonstrated causes. No failed assertion was hidden or timeout relaxed. diff --git a/docs/redesign/phase-7/p7-11/SHARED.md b/docs/redesign/phase-7/p7-11/SHARED.md index 11ac30b..b5ccdbb 100644 --- a/docs/redesign/phase-7/p7-11/SHARED.md +++ b/docs/redesign/phase-7/p7-11/SHARED.md @@ -1,5 +1,7 @@ # Shared accessibility and forms +Publication: **d8c01873053e59c896e60911b8e5c14b00693c7f**, normally pushed/directly verified20October18:26:54UTC. The following localisation/error WIP was preserved. Earlier pending-publication observations are superseded by this receipt. + Final modal continuation **5/5 PASS**,18:23:09–18:24:16UTC; combined with the separate Strategy case,26all-enabled scans have0violations/176incomplete observations:172contrast and4hidden-focus MUI menu-transition contexts. The previous dangling-title and roleless Undo/Redo observations are absent in this fresh vector. No human/device acceptance follows. **Implemented CONDITIONAL GO; final hygiene/publication pending.** Existing shell/form owners remain; no navigation or mutation protocol replacement. diff --git a/docs/redesign/phase-7/p7-11/evidence/localization-results.json b/docs/redesign/phase-7/p7-11/evidence/localization-results.json new file mode 100644 index 0000000..9a032f2 --- /dev/null +++ b/docs/redesign/phase-7/p7-11/evidence/localization-results.json @@ -0,0 +1,790 @@ +{ + "observedUtc": "2026-10-20T18:17:46.580335+00:00", + "sourceScope": "Post-run source identities; no claim of pre-run capture or that later catalogue/comment changes were in earlier exports", + "sources": { + "src/components/CvTemplateGallery.tsx": "51d082fe381a1ad7dcd17ddc374c7252e7ed1b5ac807174da8e5ac6a65acd239", + "src/components/RemindersView.tsx": "1eedbc443761001e86be1f322691ab4058553033e764b0c6cf9c93ce4fab05c0", + "src/components/ApplicationAssets.tsx": "453476dca53b2bbb898d082fdc6088426924774e21fb4114e118968d26573391", + "src/views/CorrespondenceInboxPage.tsx": "2d3a44b6e7ef665503a58506413fc9b67b6da797d31db97ef88d6c0d4a196a1e", + "src/i18n/translations.ts": "15cc09b7dfcea607b6a597d1379aaad5d3e1c01adcc518846a9047b398ba5458" + }, + "native": [ + { + "name": "localization-residual", + "stats": { + "startTime": "2026-10-20T18:15:24.291Z", + "duration": 100642.52, + "expected": 9, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "path": ".artifacts/phase7/current/ui/localization-residual.json", + "sha256": "3889f59003202e7d3400afcf7e79685d7234aebb6af8c8fd8282e5f5efea22fe", + "command": { + "name": "localization-residual", + "command": [ + "rtk", + "proxy", + "node", + "node_modules/@playwright/test/cli.js", + "test", + "e2e/phase7-cv-library.spec.ts", + "e2e/phase7-reminders.spec.ts", + "e2e/phase7-correspondence-read.spec.ts", + "e2e/phase7-correspondence-compose.spec.ts", + "--grep", + "locale-retained create|free gallery|finite library|live EN/Bokmal|eight finite bilingual|strict actual language204|strict real UI-language204", + "--retries=0", + "--reporter=list,json", + "--output=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\ui\\localization-residual-results" + ], + "startedUtc": "2026-10-20T18:15:23.736513+00:00", + "finishedUtc": "2026-10-20T18:17:05.008996+00:00", + "exitCode": 0 + } + } + ], + "images": [ + { + "name": "p7-reminders-nb-light-1440-live-locale-raw-reasons", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-light-1440-live-locale-raw-reasons-5174c4aa.png", + "sha256": "5174c4aabee046139adc1b7d5bd5ce104b2df26f741682b78688aa2eab0ba252" + }, + { + "name": "p7-reminders-en-light-320-long-four-group-rows", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-en-light-320-long-four-group-rows-1a3d139f.png", + "sha256": "1a3d139f441eb6545000cf7ae8d2bda57fffd9e5adeffe3e777e71f802378538" + }, + { + "name": "p7-reminders-nb-dark-360-long-four-group-rows", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-dark-360-long-four-group-rows-eb339e58.png", + "sha256": "eb339e581252bec77187a4ff9217fe9f6a16932b799bdb8ce7c54f3149eea624" + }, + { + "name": "p7-reminders-en-dark-390-long-four-group-rows", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-en-dark-390-long-four-group-rows-1cb5b4c0.png", + "sha256": "1cb5b4c0304cb64f81b68d2db04710b6e9bd7156e1b6d0dd10967251cf2efb16" + }, + { + "name": "p7-reminders-nb-light-768-long-four-group-rows", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-light-768-long-four-group-rows-747d5625.png", + "sha256": "747d5625d5b2a85c1434fb878778c5d3357a0378c4b7ded85657a4809472e61d" + }, + { + "name": "p7-reminders-en-dark-1024-long-four-group-rows", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-en-dark-1024-long-four-group-rows-d8b177b4.png", + "sha256": "d8b177b40404f43ae625f0fc524051c04148747340dccc38a981fff08b7252f5" + }, + { + "name": "p7-reminders-nb-light-1280-long-four-group-rows", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-light-1280-long-four-group-rows-16e0872b.png", + "sha256": "16e0872b2eb5d2367ffd5c12f313156179f2078a82ecbd307d94b5820dcde0a9" + }, + { + "name": "p7-reminders-en-light-1440-long-four-group-rows", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-en-light-1440-long-four-group-rows-b45dbb83.png", + "sha256": "b45dbb831eaa4515dd6c946a3aae2241e18a55ee80981b81504a9627a5465c10" + }, + { + "name": "p7-reminders-nb-dark-1920-long-four-group-rows", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-dark-1920-long-four-group-rows-01f44aea.png", + "sha256": "01f44aea9eaac8614aa84223ff324451f6c1cdf26645de4e0f987fb39c51bebc" + }, + { + "name": "p7-cv-library-nb-light-360-held-create", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-360-held-create-90323733.png", + "sha256": "90323733a3d443f6b874af3b4d94229aebcb9acbbc2c58683aadc64632bb4207" + }, + { + "name": "p7-cv-library-nb-light-320-library", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-320-library-073cd027.png", + "sha256": "073cd027929f7042b476bfd90fc17751ed963a6fc7892f8dc60deb972efc5342" + }, + { + "name": "p7-cv-library-nb-light-320-gallery", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-320-gallery-1010c8fe.png", + "sha256": "1010c8feb945f67adc27f1ab4b8372caafa50eda7a2ee5ad8aea877ec41050d2" + }, + { + "name": "p7-cv-library-en-dark-360-library", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-dark-360-library-57de942b.png", + "sha256": "57de942bd6fbe73fe1fd7bd1d5e28f848bae9c03ec47d8155dddd05d48e07ac9" + }, + { + "name": "p7-cv-library-en-dark-360-gallery", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-dark-360-gallery-1dce647e.png", + "sha256": "1dce647e813fcbba5608d07ec4f0b8341d479fa2b746642e49018577c00963d5" + }, + { + "name": "p7-cv-library-nb-dark-390-library", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-dark-390-library-b4b38cb8.png", + "sha256": "b4b38cb86adfaed3f9be7b4f50d9ff39e4e96161b10d9d48c66a49affa52b34e" + }, + { + "name": "p7-cv-library-nb-dark-390-gallery", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-dark-390-gallery-4687b2de.png", + "sha256": "4687b2de2c74bc28cc6c1258f18dd13db683a52c3d2b15c70bb3b078dd92525f" + }, + { + "name": "p7-cv-library-en-light-768-library", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-light-768-library-8a0b9fef.png", + "sha256": "8a0b9fefc5ce1900cc0cc900e6a3210909304122f19edb33b62dc586539848cc" + }, + { + "name": "p7-cv-library-en-light-768-gallery", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-light-768-gallery-d5f1fef5.png", + "sha256": "d5f1fef5e1bb2c233a76b2ba23b563f516c5d46dd1be69acec834f7edbfaf9ab" + }, + { + "name": "p7-cv-library-nb-light-1024-library", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-1024-library-f4cb6099.png", + "sha256": "f4cb6099cf253e2a8100ffc75ea00f3f81a97184fbe6f69004e705acf89fe2d8" + }, + { + "name": "p7-cv-library-nb-light-1024-gallery", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-1024-gallery-268fd1cf.png", + "sha256": "268fd1cfb7669c64252ec824ae00e9f9aae133b754f0075dfa96f92c87ef2880" + }, + { + "name": "p7-cv-library-en-dark-1280-library", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-dark-1280-library-4e8f38a4.png", + "sha256": "4e8f38a468c454e053d4ce2fa9bef76dac1daa33b75be6f1eebf450220d9f3dd" + }, + { + "name": "p7-cv-library-en-dark-1280-gallery", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-dark-1280-gallery-cd1a90bd.png", + "sha256": "cd1a90bd628f6c8946d95f89b52a48bd098ccc0d1e0a121eda13b637321e28b7" + }, + { + "name": "p7-cv-library-nb-dark-1440-library", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-dark-1440-library-e83ac07e.png", + "sha256": "e83ac07eeedbbf82fa8c200f700ab96debb01a7a0fee53e67d4a71300895678b" + }, + { + "name": "p7-cv-library-nb-dark-1440-gallery", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-dark-1440-gallery-36708ab6.png", + "sha256": "36708ab6d315803382962458c27896e1ad00f4e480e88f667d30b3db10f7cd6d" + }, + { + "name": "p7-cv-library-en-light-1920-library", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-light-1920-library-486e60b3.png", + "sha256": "486e60b38066f8113684087a240bf3244d21a69af631654257b0712ddfa1840b" + }, + { + "name": "p7-cv-library-en-light-1920-gallery", + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-light-1920-gallery-1cf64a79.png", + "sha256": "1cf64a79bdbe4e16fa2073317f1dc675573f4d7fcd4725a96cfe4c149b6b1a0c" + }, + { + "name": "p706-read-nb-light-1440-strict-locale-preserved-saved-detail", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-light-1440-strict-locale-preserved-saved-detail-4a449716.png", + "sha256": "4a4497167b8d3dd221623f16bf1078502b0f2022a24d3b195dab24a874c432cb" + }, + { + "name": "p706-read-en-light-1440-strict-locale-dirty-review-notes", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-light-1440-strict-locale-dirty-review-notes-e3fd5780.png", + "sha256": "e3fd57804f529bfaaa4a046654ac528504338435e637b90c36b90af4c5817909" + }, + { + "name": "p706-read-en-light-320-long-inbox", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-light-320-long-inbox-a94adc4b.png", + "sha256": "a94adc4bb4a0efadf34063739776212c279ee1f837c7888f8fe8e8984e750034" + }, + { + "name": "p706-read-nb-dark-360-long-review", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-dark-360-long-review-887edc93.png", + "sha256": "887edc93f62989127fc027a84095681f26ea1bf96f74c424b9b2b89a35e4548b" + }, + { + "name": "p706-read-en-dark-390-long-detail", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-dark-390-long-detail-f715ae3c.png", + "sha256": "f715ae3cf273100801d88ec57cfff66b63f23be0f938c8109fce1285d0e05ab4" + }, + { + "name": "p706-read-nb-light-768-long-inbox", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-light-768-long-inbox-c08361be.png", + "sha256": "c08361be8aeefdacf43ef677c02b5a47aad5b1e120e61ed398fadd9bb23855dd" + }, + { + "name": "p706-read-en-dark-1024-long-review", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-dark-1024-long-review-9853b2fe.png", + "sha256": "9853b2fe95f84a910b89864d2b15b9a277838cbf5dba0431479153211d784b88" + }, + { + "name": "p706-read-nb-light-1280-long-detail", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-light-1280-long-detail-7100a3d0.png", + "sha256": "7100a3d05e42f03e36033207f2ca40ef0856f0d764d03be89d44e6406ce57730" + }, + { + "name": "p706-read-en-light-1440-long-review", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-light-1440-long-review-c3e53f38.png", + "sha256": "c3e53f3899a814ab1c7829aedc9ed269bc4a04481de64b1e05d543fb41505f7f" + }, + { + "name": "p706-read-nb-dark-1920-long-inbox", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-dark-1920-long-inbox-c9aa052b.png", + "sha256": "c9aa052bcd0f02c4ac34ec079c99787ed53b8f30fa1bd18c525882f666f1dfa9" + }, + { + "name": "p706-compose-nb-light-1440-strict-language-dirty-draft", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-1440-strict-language-dirty-draft-f7f74362.png", + "sha256": "f7f74362d765fb790ed0944ecc7616b83ae602b4866c9c34015478b22de22ecb" + }, + { + "name": "p706-compose-en-light-320-long-draft-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-light-320-long-draft-keyboard-263be757.png", + "sha256": "263be757bdefe0da8649918b6adb1d049900735539ba4516e92fc99f39d7ec43" + }, + { + "name": "p706-compose-en-light-320-owned-setup-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-light-320-owned-setup-keyboard-66af59e4.png", + "sha256": "66af59e4b1a7a9178c61f2e8ca761b8bb01348ac6e3b43c6be9beb4452b664b9" + }, + { + "name": "p706-compose-nb-dark-360-long-draft-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-dark-360-long-draft-keyboard-bfe733b5.png", + "sha256": "bfe733b572db285b85b8a8bca041f5dba4ac0c20c5b42224a73629f4e3d1dd1a" + }, + { + "name": "p706-compose-en-dark-390-long-draft-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-dark-390-long-draft-keyboard-b4c1f542.png", + "sha256": "b4c1f5421c30eff3c94340977475173c3ffd560891eac640808fd8e0ee7d41a2" + }, + { + "name": "p706-compose-en-dark-390-send-review-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-dark-390-send-review-keyboard-eb37211b.png", + "sha256": "eb37211be24aa9fdef32470824f508b8e4f594f19b5b2a8150236662edeaddc3" + }, + { + "name": "p706-compose-en-390-focused-viewport", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-390-focused-viewport-b93c4213.png", + "sha256": "b93c42133c6d18f195e5706d4c4163aac1c1ec0e9569f5c0997a9113497b77a5" + }, + { + "name": "p706-compose-nb-light-768-long-draft-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-768-long-draft-keyboard-67d78961.png", + "sha256": "67d78961f6211ef171a3df769fe5894232367fa76105b287754160de7f734d7c" + }, + { + "name": "p706-compose-nb-light-768-owned-setup-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-768-owned-setup-keyboard-816c6e7a.png", + "sha256": "816c6e7a09c523a2c9c52733fd003f1b7d1425a34e9f15ae16405499ae23b82e" + }, + { + "name": "p706-compose-en-dark-1024-long-draft-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-dark-1024-long-draft-keyboard-2fe4e29c.png", + "sha256": "2fe4e29c71edadcb93829cb35f94041f48fa660c59ac752ba9783e05a4f2ddb0" + }, + { + "name": "p706-compose-nb-light-1280-long-draft-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-1280-long-draft-keyboard-318dca1a.png", + "sha256": "318dca1adf2201b2a5305b8535b2f9faf08622f4ed65eeb144ed3584785c94fd" + }, + { + "name": "p706-compose-nb-light-1280-send-review-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-1280-send-review-keyboard-c81f7b6a.png", + "sha256": "c81f7b6ad2bb388def37ae69eb2d8d866d47ab60100655a8993f5fa59ed6dea0" + }, + { + "name": "p706-compose-nb-1280-focused-viewport", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-1280-focused-viewport-287c9542.png", + "sha256": "287c95427ee3ae4aa43e69a06c6215f57ed35da72c2efcb1e3c05e64fdfd955d" + }, + { + "name": "p706-compose-en-light-1440-long-draft-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-light-1440-long-draft-keyboard-c3502140.png", + "sha256": "c35021406cb9c69b55edbb7ef923fc784c11e72ebbd6cd4d7cc7a3bcbe39a496" + }, + { + "name": "p706-compose-en-light-1440-owned-setup-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-light-1440-owned-setup-keyboard-88330187.png", + "sha256": "88330187db1420131b05e672bbb6580055b8a03a234122028ddab2ece8906ddc" + }, + { + "name": "p706-compose-nb-dark-1920-long-draft-keyboard", + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-dark-1920-long-draft-keyboard-80054d2f.png", + "sha256": "80054d2fc5460c82bdab8453aff70307b6eed77c62b733d0be4b0e4062b2c35b" + } + ], + "scans": [ + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-light-1440-live-locale-raw-reasons-audit-51fb774c.json", + "sha256": "51fb774c6b8b877267d3e5fb4527854e39cd24464ad5b3119dd288aa7fb006f8", + "violations": 0, + "incompleteNodes": 2, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-en-light-320-long-four-group-rows-audit-0f06e3b3.json", + "sha256": "0f06e3b3cfd38456b25256607a990cd26840468c3d7896fcfc9a9870103f4806", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-dark-360-long-four-group-rows-audit-2a3072cb.json", + "sha256": "2a3072cb2edd1a06bde4dcd7253a49a5c91538356e98212b1485da8640bdb297", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-en-dark-390-long-four-group-rows-audit-cd3baa00.json", + "sha256": "cd3baa00e21cd79db255f2648b9a4a1fb1f88d827d374a08ab1c9811c3ec699e", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-light-768-long-four-group-rows-audit-bb464ca5.json", + "sha256": "bb464ca5bdb9ade0f8a6d5dde0bb9cb47b319a0e897e501cdd41992b1d1568ea", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-en-dark-1024-long-four-group-rows-audit-0efb5577.json", + "sha256": "0efb5577526eb9225a14ef226cc65cd766e50586cb4da12de9bd614073174bc2", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-light-1280-long-four-group-rows-audit-645dc2ed.json", + "sha256": "645dc2ed8ecf78ad072abd475800851bcb3099d1d78ece23d314b65321d9c45f", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-en-light-1440-long-four-group-rows-audit-7ea9f77c.json", + "sha256": "7ea9f77cfe5a415c99740a985593f43b67d2abe4d04048fbc28c5083d1d13f1f", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-reminders-nb-dark-1920-long-four-group-rows-audit-cc9f2c61.json", + "sha256": "cc9f2c61532fc140226fab8fc31a19c890092c6a0b92a4370e3bae4fffd88468", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-360-held-create-audit-3a04077a.json", + "sha256": "3a04077a8d10f9c7138434dcc90ddb5a73c5fa130a32d94bdcd62ffb84def43d", + "violations": 0, + "incompleteNodes": 4, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-320-library-audit-9489d693.json", + "sha256": "9489d693d8c321ac6686fc0cfbf4c83b0317843c3bf3d14e7424f129b3df03bf", + "violations": 0, + "incompleteNodes": 2, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-320-gallery-audit-d8a29224.json", + "sha256": "d8a292243e7169b6c1bad338cd7a104dbcc9868aa44935a7598c8218cbe67bbd", + "violations": 0, + "incompleteNodes": 6, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-dark-360-library-audit-a896a26f.json", + "sha256": "a896a26f8387c60e5efe86bfe800f29e47a25cf401d7ce597c475b87c07060cf", + "violations": 0, + "incompleteNodes": 2, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-dark-360-gallery-audit-80318eb4.json", + "sha256": "80318eb47893a506554599047b01178860eb0d16ed37c20b8facb5721d0fd895", + "violations": 0, + "incompleteNodes": 5, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-dark-390-library-audit-2d7964d9.json", + "sha256": "2d7964d97922d1c0ad7dbce2e62886fce9aa4c6900e2f2e137cb88e2f2d0d0b7", + "violations": 0, + "incompleteNodes": 2, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-dark-390-gallery-audit-03b683be.json", + "sha256": "03b683be0b7490575f418dddb4bd0a3193660187f911d6b2bc0d3af253bf160b", + "violations": 0, + "incompleteNodes": 6, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-light-768-library-audit-d5897bed.json", + "sha256": "d5897bed68a7f17ac01da4be0e79a8edc079fce8ffe8b03213e435b744689e38", + "violations": 0, + "incompleteNodes": 2, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-light-768-gallery-audit-32f3e76b.json", + "sha256": "32f3e76b41903b307801ea2c04b21ecdcd52ab510851bc51c56cebd26f5f7695", + "violations": 0, + "incompleteNodes": 5, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-1024-library-audit-bdb0cf0e.json", + "sha256": "bdb0cf0ee01924842773abbc7392e403caf3a6ad842ea0bb4ef42aca759e90a5", + "violations": 0, + "incompleteNodes": 2, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-light-1024-gallery-audit-e882952a.json", + "sha256": "e882952a17921011c6cd15e45bf9452f8b792bbf2ddd442fb5004017905af669", + "violations": 0, + "incompleteNodes": 5, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-dark-1280-library-audit-39c8726a.json", + "sha256": "39c8726ab2e1892e15670798da2368893285d9058be2bf59137f6f2b405c1302", + "violations": 0, + "incompleteNodes": 2, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-dark-1280-gallery-audit-39f0db39.json", + "sha256": "39f0db39a11376f11076024a2d5c3ed40ec6df65aca1f3ad4bfb5bee4c262144", + "violations": 0, + "incompleteNodes": 5, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-dark-1440-library-audit-401c6c4d.json", + "sha256": "401c6c4d87458407bcd8ac0ec641d3d0acde973f3f43e12406c8c050ffb941ce", + "violations": 0, + "incompleteNodes": 2, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-nb-dark-1440-gallery-audit-203d78e8.json", + "sha256": "203d78e849531012f7c88a182b2ef4b3f9ce77011b72925317769873522e8634", + "violations": 0, + "incompleteNodes": 5, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-light-1920-library-audit-269f608c.json", + "sha256": "269f608c0cd041f5f45624677c5dae264c089f8e3385249487bcf7ec24794825", + "violations": 0, + "incompleteNodes": 2, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p7-cv-library-en-light-1920-gallery-audit-031be6ed.json", + "sha256": "031be6ed8c957cdc43f2ec53f08aad3d6c866f8456f11e0e67ddbc01fe97ae59", + "violations": 0, + "incompleteNodes": 5, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-light-1440-strict-locale-preserved-saved-detail-audit-efb0ab77.json", + "sha256": "efb0ab77848f8692faf0d0975d7ee5a4f7bc8f2b0fad3c901e9a26fd5a80f7eb", + "violations": 0, + "incompleteNodes": 6, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-light-1440-strict-locale-dirty-review-notes-audit-4265ce03.json", + "sha256": "4265ce03a8813f0d8f4b61d5ee4939d78eb76cee1f714318e34afb34b7f95aec", + "violations": 0, + "incompleteNodes": 6, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-light-320-long-inbox-audit-50f8f83c.json", + "sha256": "50f8f83c2ab49ea0b30fd5fa19dad8c39719d60a9b904c3dfe8e9d3ac933315a", + "violations": 0, + "incompleteNodes": 4, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-dark-360-long-review-audit-be3aec8f.json", + "sha256": "be3aec8fde7c23be9fb2aa478db45ad5d76e2d5607804bbb30292ad2c18699c7", + "violations": 0, + "incompleteNodes": 3, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-dark-390-long-detail-audit-9f0a7539.json", + "sha256": "9f0a75394a28afaf5ce23389f0f6295325640aa19ed5978a34b2e65813e2ad3f", + "violations": 0, + "incompleteNodes": 4, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-light-768-long-inbox-audit-254b0273.json", + "sha256": "254b0273b6613be3d881b2d41e0e5f6d5a5815d2b77d1f62670bab802564dfa2", + "violations": 0, + "incompleteNodes": 4, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-dark-1024-long-review-audit-38f2af6c.json", + "sha256": "38f2af6cb7afdb5f75769f68e7a567f0ecc532c7ab1bb5f3b9db48048b4a0327", + "violations": 0, + "incompleteNodes": 3, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-light-1280-long-detail-audit-f194a43a.json", + "sha256": "f194a43a4d076d0f351fca7c58004c152d8698d28916a0b2e589bc02cba77634", + "violations": 0, + "incompleteNodes": 4, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-en-light-1440-long-review-audit-fe447c3d.json", + "sha256": "fe447c3d612fa4dfa8fd89fd02a79b1d6535c446870b74a3c796d0e924e95634", + "violations": 0, + "incompleteNodes": 3, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-read-nb-dark-1920-long-inbox-audit-0bccb006.json", + "sha256": "0bccb006105ba6e269d3b8e119a702ccd598d597fc5284e1f2354c4acaf3fcd1", + "violations": 0, + "incompleteNodes": 4, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-1440-strict-language-dirty-draft-audit-153568d2.json", + "sha256": "153568d2e461ebb52d0646a710edee0ea11cdc72997ddc9edd5e36e41f4c9656", + "violations": 0, + "incompleteNodes": 11, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-light-320-long-draft-keyboard-audit-b3f8f90d.json", + "sha256": "b3f8f90d8d715c0c96886efa2f1abdb9d9c9e69bac79789f778b39da085f36b8", + "violations": 0, + "incompleteNodes": 10, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-light-320-owned-setup-keyboard-audit-65797a52.json", + "sha256": "65797a523e148efc6eaefb9c8063f95c029e367efde1d0291735f17c843addbd", + "violations": 0, + "incompleteNodes": 13, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-dark-360-long-draft-keyboard-audit-a35ebf17.json", + "sha256": "a35ebf171b7ed2973bc2428f689fffb83424466027e6388ca79dac9fede89d97", + "violations": 0, + "incompleteNodes": 10, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-dark-390-long-draft-keyboard-audit-9b7f1f51.json", + "sha256": "9b7f1f51c6f0d3feb1bd94d9923ffa4feee0a8c6f4ccac83056c37319f518e55", + "violations": 0, + "incompleteNodes": 10, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-dark-390-send-review-keyboard-audit-1fd29547.json", + "sha256": "1fd29547d441f7857cc1310ac895aedb1c0e15c0d3464c14a0b82041fc719f5b", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-768-long-draft-keyboard-audit-06e4c6c9.json", + "sha256": "06e4c6c9450419f8c1de35cde0593ae053151910cb6edc064159e54eda6f377a", + "violations": 0, + "incompleteNodes": 10, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-768-owned-setup-keyboard-audit-c5fecd9b.json", + "sha256": "c5fecd9b2a1306747dade88d53714d5162f17909a4d7eb676b0ba6a48891c8be", + "violations": 0, + "incompleteNodes": 13, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-dark-1024-long-draft-keyboard-audit-839b1fe3.json", + "sha256": "839b1fe3204e12ae95333dafaa6090f81b38fe13ee7a4481a3a15d0d4ccd9eaf", + "violations": 0, + "incompleteNodes": 10, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-1280-long-draft-keyboard-audit-cdccb40b.json", + "sha256": "cdccb40b2fd830f5cad43e936d0403b55b681bec48270356ce5a072715719a8a", + "violations": 0, + "incompleteNodes": 10, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-light-1280-send-review-keyboard-audit-ee1dd0e7.json", + "sha256": "ee1dd0e7572f455a2ddf538017982451ec1fa8399e5c4dbf25d487bd57a02629", + "violations": 0, + "incompleteNodes": 12, + "incompleteRules": [ + "aria-hidden-focus", + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-light-1440-long-draft-keyboard-audit-a560b64c.json", + "sha256": "a560b64ca441dc14bda4a41fe641144ba3cf292106fff46d2834ab9ad72ba1e3", + "violations": 0, + "incompleteNodes": 10, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-en-light-1440-owned-setup-keyboard-audit-c355f1dc.json", + "sha256": "c355f1dc15c020a27a55f8e0801b943b72d9c719992a5f423cb7e5908c4cf110", + "violations": 0, + "incompleteNodes": 13, + "incompleteRules": [ + "color-contrast" + ] + }, + { + "path": ".artifacts/phase7/p7-11/localization-visuals/p706-compose-nb-dark-1920-long-draft-keyboard-audit-b166fa0c.json", + "sha256": "b166fa0c14493ba6ec4ee820ed187bc216e3fcf10cf9044a19e4be5a5030a1d2", + "violations": 0, + "incompleteNodes": 10, + "incompleteRules": [ + "color-contrast" + ] + } + ], + "focusedUnit": { + "path": ".artifacts/phase7/p7-11/localization/green.json", + "sha256": "cc6e8a986cbfdc707299fee09b9eb10a130afe96caf5805a1fdf4d206f70e8bd", + "numPassedTests": 76, + "numFailedTests": 0, + "numPendingTests": 0, + "numPassedTestSuites": 4, + "qualification": "Overlapping focused vectors; not a new full-suite total. Final integrated run covers subsequent documented corrections." + } +} diff --git a/docs/redesign/phase-7/p7-11/evidence/localization-static-checks.json b/docs/redesign/phase-7/p7-11/evidence/localization-static-checks.json new file mode 100644 index 0000000..1db92b7 --- /dev/null +++ b/docs/redesign/phase-7/p7-11/evidence/localization-static-checks.json @@ -0,0 +1,104 @@ +{ + "schemaVersion": 1, + "checkpoint": "p7-11", + "status": "PASS", + "checkedUtc": "2026-10-20T18:27:31.645877+00:00", + "commands": [ + { + "name": "secret-scan", + "command": [ + "rtk", + "proxy", + "C:\\Users\\Connor\\AppData\\Local\\Programs\\Python\\Python312\\python.exe", + "-X", + "utf8", + "scripts/supply-chain.py", + "scan-secrets" + ], + "startedUtc": "2026-10-20T18:27:28.216719+00:00", + "finishedUtc": "2026-10-20T18:27:30.845935+00:00", + "exitCode": 0, + "log": ".artifacts/phase7/current/static/20261020T182728Z/secret-scan.log", + "logSha256Raw": "be5119e6057287d397e87759f855c91e634e3bfac61c8be7584d05592ba2dfcd" + }, + { + "name": "working-whitespace", + "command": [ + "rtk", + "proxy", + "git", + "diff", + "--check" + ], + "startedUtc": "2026-10-20T18:27:30.846994+00:00", + "finishedUtc": "2026-10-20T18:27:30.947638+00:00", + "exitCode": 0, + "log": ".artifacts/phase7/current/static/20261020T182728Z/working-whitespace.log", + "logSha256Raw": "228a53f8fb33d6295717d33413b91ed30e8b6c8798498c0712bf14ad41f33b65" + }, + { + "name": "staged-whitespace", + "command": [ + "rtk", + "proxy", + "git", + "diff", + "--cached", + "--check" + ], + "startedUtc": "2026-10-20T18:27:30.947638+00:00", + "finishedUtc": "2026-10-20T18:27:31.037564+00:00", + "exitCode": 0, + "log": ".artifacts/phase7/current/static/20261020T182728Z/staged-whitespace.log", + "logSha256Raw": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "phase7-whitespace", + "command": [ + "rtk", + "proxy", + "git", + "diff", + "--check", + "b5319981ad3d1bd635daff861453d23c0de5320b" + ], + "startedUtc": "2026-10-20T18:27:31.038587+00:00", + "finishedUtc": "2026-10-20T18:27:31.226719+00:00", + "exitCode": 0, + "log": ".artifacts/phase7/current/static/20261020T182728Z/phase7-whitespace.log", + "logSha256Raw": "34416ce3f522883664d5c28bf78c7cff2163564949ca86327bd5ece568ce1e56" + } + ], + "untrackedSecretScan": { + "files": 4, + "findings": 0 + }, + "documentation": { + "files": 38, + "relativeLinks": 645, + "errors": [] + }, + "preservation": { + "groups": 57, + "statuses": { + "PARTIAL": 56, + "PASS": 0, + "FAIL": 0, + "DEFERRED": 1 + }, + "originalRoutesPreserved": true, + "currentConcreteRoutes": 33 + }, + "production": { + "main": "f7dc22e5e7efe1fd1d64f6ace2c04cf2f95bf558", + "originMain": "f7dc22e5e7efe1fd1d64f6ace2c04cf2f95bf558", + "status": "NO-GO", + "qualification": "Local fetched refs; direct push observation recorded separately." + }, + "hashPolicy": "Portable JSON UTF8 LF; raw log hashes measure exact saved bytes.", + "limits": [ + "Pattern scanner skips binary and files over2MiB; not historical Git/production secrets.", + "Local links check target existence, not URL/heading anchors.", + "Static evidence is not runtime/manual/device/provider/production acceptance." + ] +} diff --git a/job-tracker-ui/e2e/phase7-cv-library.spec.ts b/job-tracker-ui/e2e/phase7-cv-library.spec.ts index 9a1f810..86ebc9f 100644 --- a/job-tracker-ui/e2e/phase7-cv-library.spec.ts +++ b/job-tracker-ui/e2e/phase7-cv-library.spec.ts @@ -216,7 +216,7 @@ test('[P7-04 PRES-C05 C08 E08] locale-retained create draft locks during held fa await expect(page.locator('html')).toHaveAttribute('lang', 'nb-NO'); dialog = await openCreate(page, 'nb'); await expect(dialog.getByLabel(translations.nb.cvEditorName, { exact: false })).toHaveValue(name); - await expect(dialog.getByRole('button', { name: selected.name, exact: true })).toHaveAttribute('aria-pressed', 'true'); + await expect(dialog.getByRole('button', { name: translations.nb.cvTemplateMinimalName, exact: true })).toHaveAttribute('aria-pressed', 'true'); expect({ listReads, themeReads }).toEqual(readsBeforeLocale); try { await dialog.getByRole('button', { name: translations.nb.cvDashboardCreateCv, exact: true }).focus(); await page.keyboard.press('Enter'); @@ -225,7 +225,7 @@ test('[P7-04 PRES-C05 C08 E08] locale-retained create draft locks during held fa await expect(dialog.getByLabel(translations.nb.cvEditorName, { exact: false })).toBeDisabled(); await expect(dialog.getByRole('button', { name: translations.nb.cancel, exact: true })).toBeDisabled(); await expect(dialog.getByRole('button', { name: translations.nb.cvEditorAtsTemplates, exact: true })).toBeDisabled(); - await expect(dialog.getByRole('button', { name: selected.name, exact: true })).toHaveAttribute('aria-disabled', 'true'); + await expect(dialog.getByRole('button', { name: translations.nb.cvTemplateMinimalName, exact: true })).toHaveAttribute('aria-disabled', 'true'); await page.keyboard.press('Enter'); await page.keyboard.press('Escape'); await expect(dialog).toBeVisible(); expect(payloads).toHaveLength(1); const content = dialog.getByRole('region', { name: translations.nb.cvDashboardCreateTitle, exact: true }); @@ -244,7 +244,7 @@ test('[P7-04 PRES-C05 C08 E08] locale-retained create draft locks during held fa } finally { release(); } await expect(dialog.getByText('Controlled create unavailable', { exact: true })).toBeVisible(); await expect(dialog.getByLabel(translations.nb.cvEditorName, { exact: false })).toHaveValue(name); - await expect(dialog.getByRole('button', { name: selected.name, exact: true })).toHaveAttribute('aria-pressed', 'true'); + await expect(dialog.getByRole('button', { name: translations.nb.cvTemplateMinimalName, exact: true })).toHaveAttribute('aria-pressed', 'true'); const createdResponse = page.waitForResponse(response => response.url() === `${apiUrl}/cv/variants` && response.request().method() === 'POST'); await dialog.getByRole('button', { name: translations.nb.cvDashboardCreateCv, exact: true }).click(); const created = await json(await createdResponse); diff --git a/job-tracker-ui/e2e/phase7-reminders.spec.ts b/job-tracker-ui/e2e/phase7-reminders.spec.ts index dc07cf3..6d2d9b8 100644 --- a/job-tracker-ui/e2e/phase7-reminders.spec.ts +++ b/job-tracker-ui/e2e/phase7-reminders.spec.ts @@ -98,6 +98,20 @@ function action(row: JobApplication, language: Sample['language'] = 'en') { : signal.actionKey === 'interview-prep' ? t.jobTableInterviewStage : signal.actionKey === 'follow-up' ? t.jobTableFollowUp : t.jobTableReadiness; return `${label}: ${row.jobTitle}`; } +function fixtureReason(row: JobApplication, language: Sample['language']) { + if (language === 'en') return row.workflowSignal!.reason; + const t = translations[language]; + switch (row.workflowSignal!.actionKey) { + case 'package-work': return t.reminderReasonCvAndAnswers; + case 'interview-prep': return t.reminderReasonInterview; + case 'review-readiness': return t.reminderReasonNoGaps; + default: { + const feedback = /^Feedback requested (\d+)d ago$/.exec(row.workflowSignal!.reason); + expect(feedback).not.toBeNull(); + return text(t.reminderReasonFeedbackDays, { days: feedback![1] }); + } + } +} function requests(session: Session, method: string, path: string) { return session.requests.filter(request => request.method === method && request.path === path); } function hold() { let release!: () => void; const pending = new Promise(resolve => { release = resolve; }); return { pending, release }; } async function keyboard(page: Page, control: Locator) { await control.focus(); await expect(control).toBeFocused(); await page.keyboard.press('Enter'); } @@ -261,7 +275,7 @@ test('[P7-05 Reminders] committed PATCH204 followed by a failed reload retries o } finally { gate.release(); } }); -test('[P7-05 Reminders] live EN/Bokmal translates status/date controls and preserves raw reasons without a page-triggered refetch', async ({ page, session }, info) => { +test('[P7-05 Reminders] live EN/Bokmal translates status/date controls and generated reasons without a page-triggered refetch', async ({ page, session }, info) => { test.setTimeout(90_000); const { rows } = await groups(page.request, session.owner); await enter(page, session.owner); await expect(region(page).getByRole('article')).toHaveCount(4); await expect.poll(() => requests(session, 'GET', '/api/jobapplications/reminders').length).toBeGreaterThanOrEqual(2); @@ -269,7 +283,7 @@ test('[P7-05 Reminders] live EN/Bokmal translates status/date controls and prese const preference = response(page, 'PUT', `${apiUrl}/auth/preferences/language`); await keyboard(page, page.locator('header').getByRole('button', { name: 'Norsk', exact: true })); expect((await preference).status()).toBe(204); await expect(page.locator('html')).toHaveAttribute('lang', 'nb-NO'); await expect(region(page, 'nb').getByRole('article')).toHaveCount(4); - for (const row of rows) await expect(article(page, row.jobTitle)).toContainText(row.workflowSignal!.reason); + for (const row of rows) await expect(article(page, row.jobTitle)).toContainText(fixtureReason(row, 'nb')); await expect(article(page, rows[1].jobTitle).getByText(translations.nb.statusInterview, { exact: true })).toBeVisible(); const authoritative = (await read(page.request)).find(row => row.id === rows[3].id)!; const date = await page.evaluate(value => new Date(value).toLocaleDateString('nb-NO'), authoritative.followUpAt!); @@ -297,7 +311,7 @@ test('[P7-05 Reminders] eight finite bilingual theme widths preserve long rows a await enter(page, session.owner, sample); await expect(region(page, sample.language).getByRole('article')).toHaveCount(4); for (const row of rows) { await expect(article(page, row.jobTitle).getByRole('heading', { level: 4 })).toContainText(row.jobTitle); - await expect(article(page, row.jobTitle)).toContainText(row.workflowSignal!.reason); + await expect(article(page, row.jobTitle)).toContainText(fixtureReason(row, sample.language)); const open = article(page, row.jobTitle).getByRole('button', { name: action(row, sample.language), exact: true }); await open.scrollIntoViewIfNeeded(); await open.focus(); await expect(open).toBeFocused(); for (const days of [3, 7]) { await snooze(page, row, days, sample.language).focus(); await expect(snooze(page, row, days, sample.language)).toBeFocused(); } diff --git a/job-tracker-ui/src/application-assets.test.tsx b/job-tracker-ui/src/application-assets.test.tsx index ef1b3b1..5a24c99 100644 --- a/job-tracker-ui/src/application-assets.test.tsx +++ b/job-tracker-ui/src/application-assets.test.tsx @@ -3,7 +3,7 @@ import "@testing-library/jest-dom"; import { act, fireEvent, render as rtlRender, screen, waitFor, within } from "@testing-library/react"; import { - ApplicationCoverLetterSection, ApplicationCvSection, ApplicationPackageDraftsSection, SubmittedPackagesSection, + ApplicationCoverLetterSection, ApplicationCvSection, ApplicationPackageDraftsSection, ApplicationTailoringSection, SubmittedPackagesSection, } from "./components/ApplicationAssets"; import { api } from "./api"; import { I18nProvider } from "./i18n/I18nProvider"; @@ -104,6 +104,23 @@ function deferred() { return { promise, resolve, reject }; } +test("[P7-11 tailoring] generated metadata and known history enums follow locale while evidence, unknown metadata and dirty prose stay verbatim", async () => { + routeGet({ tailoring: { ...tailoring, suggestions: [...tailoring.suggestions, { kind: "custom", title: "My custom title", detail: "My custom detail", items: ["My verified evidence"] }] }, coverLetter: { ...coverLetter, versions: [...coverLetter.versions, { version: 0, source: "custom-source", aiAction: "custom-action", length: 5, createdAtUtc: "2026-07-19T09:00:00Z", isCurrent: false }] } }); + render(<>); + await screen.findByText("Skills to highlight"); + const letter = await screen.findByRole("textbox", { name: "Cover letter" }); + fireEvent.change(letter, { target: { value: "My unsaved English letter" } }); + const reads = mockedApi.get.mock.calls.length; + changeLanguage("nb"); + expect(screen.getByText(copy("assetsTailoringHighlightTitle"))).toBeInTheDocument(); + expect(screen.getByText(copy("assetsTailoringHighlightDetail"))).toBeInTheDocument(); + expect(screen.getByText(`${copy("historySourceAi")} · ${copy("coverActionImprove")}`)).toBeInTheDocument(); + expect(screen.getByText("custom-source · custom-action")).toBeInTheDocument(); + for (const text of ["My custom title", "My custom detail", "My verified evidence", "C#", "SQL"]) expect(screen.getByText(text)).toBeInTheDocument(); + expect(screen.getByRole("textbox", { name: copy("jobDetailsCoverLetter") })).toHaveValue("My unsaved English letter"); + expect(mockedApi.get).toHaveBeenCalledTimes(reads); expect(mockedApi.put).not.toHaveBeenCalled(); expect(mockedApi.post).not.toHaveBeenCalled(); +}); + test("document package history is read-only and separates captured notes from answers", async () => { const summary = { id: 91, version: 1, cvVariantId: 3, cvVariantName: "Original CV", cvVariantVersion: 2, cvThemeId: "nordic", hasCv: false, hasCoverLetter: true, createdAtUtc: "2026-07-19T10:00:00Z", attachments: [] }; mockedApi.get.mockImplementation((url: string) => Promise.resolve({ data: url.endsWith('/91') ? { @@ -197,7 +214,7 @@ test("cover letter loads the current text and its history", async () => { expect(await screen.findByDisplayValue("Dear team")).toBeInTheDocument(); expect(screen.getByText("v2")).toBeInTheDocument(); - expect(screen.getByText("ai · improve")).toBeInTheDocument(); + expect(screen.getByText("AI suggestion · Improve")).toBeInTheDocument(); expect(screen.getByText("Current")).toBeInTheDocument(); }); diff --git a/job-tracker-ui/src/components/ApplicationAssets.tsx b/job-tracker-ui/src/components/ApplicationAssets.tsx index afa0103..cd00658 100644 --- a/job-tracker-ui/src/components/ApplicationAssets.tsx +++ b/job-tracker-ui/src/components/ApplicationAssets.tsx @@ -404,6 +404,15 @@ export function SubmittedPackagesSection({ jobId, allowCapture = true }: { jobId // ---------- Tailoring ---------- +const tailoringLabels = new Map([ + ["highlight-skills", ["assetsTailoringHighlightTitle", "assetsTailoringHighlightDetail"]], + ["prioritise-experience", ["assetsTailoringExperienceTitle", "assetsTailoringExperienceDetail"]], + ["emphasise-projects", ["assetsTailoringProjectsTitle", "assetsTailoringProjectsDetail"]], + ["career-evidence", ["assetsTailoringEvidenceTitle", "assetsTailoringEvidenceDetail"]], + ["include-keywords", ["assetsTailoringKeywordsTitle", "assetsTailoringKeywordsDetail"]], + ["gaps", ["assetsTailoringGapsTitle", "assetsTailoringGapsDetail"]], +]); + export function ApplicationTailoringSection({ jobId }: { jobId: number }) { const { t } = useI18n(); const { data, error, loading, reload } = useAsset( @@ -437,11 +446,13 @@ export function ApplicationTailoringSection({ jobId }: { jobId: number }) { {t("assetsNoSuggestions")} ) : ( - (data?.suggestions ?? []).map((s) => ( + (data?.suggestions ?? []).map((s) => { + const labels = tailoringLabels.get(s.kind); + return ( - {s.title} + {labels ? t(labels[0]) : s.title} {s.detail && ( - {s.detail} + {labels ? t(labels[1]) : s.detail} )} {s.items.map((item) => ( @@ -449,7 +460,7 @@ export function ApplicationTailoringSection({ jobId }: { jobId: number }) { ))} - )) + ); }) )} @@ -458,6 +469,18 @@ export function ApplicationTailoringSection({ jobId }: { jobId: number }) { // ---------- Cover letter ---------- +const historySources = new Map([ + ["manual", "historySourceManual"], ["ai", "historySourceAi"], + ["template", "historySourceTemplate"], ["restore", "historySourceRestore"], +]); +const historyAiActions = new Map([ + ["generate", "coverActionGenerate"], ["regenerate", "coverActionRegenerate"], + ["improve", "coverActionImprove"], ["shorten", "coverActionShorten"], + ["expand", "coverActionExpand"], ["professional", "coverActionProfessional"], + ["natural", "coverActionNatural"], ["grammar", "coverActionGrammar"], + ["tone", "coverAiTone"], ["tailor", "coverActionTailor"], +]); + const COVER_LETTER_TEMPLATES = { en: `Dear Hiring Manager, @@ -615,7 +638,12 @@ export function ApplicationCoverLetterSection({ jobId, onDirtyChange }: { jobId: ) : ( - {(data?.versions ?? []).map((v) => ( + {(data?.versions ?? []).map((v) => { + const sourceKey = historySources.get(v.source); + const actionKey = historyAiActions.get(v.aiAction ?? ""); + const source = sourceKey ? t(sourceKey) : v.source; + const action = actionKey ? t(actionKey) : v.aiAction; + return ( v{v.version} - + {v.isCurrent && } @@ -648,7 +676,7 @@ export function ApplicationCoverLetterSection({ jobId, onDirtyChange }: { jobId: )} - ))} + ); })} )} diff --git a/job-tracker-ui/src/components/CvTemplateGallery.tsx b/job-tracker-ui/src/components/CvTemplateGallery.tsx index 94ba8ce..9f12d50 100644 --- a/job-tracker-ui/src/components/CvTemplateGallery.tsx +++ b/job-tracker-ui/src/components/CvTemplateGallery.tsx @@ -4,6 +4,7 @@ import { Box, Button, Chip, Paper, Stack, Typography } from "@mui/material"; import { CvTheme, getVisibleCvThemes } from "../cvBuilder"; import { useI18n } from "../i18n/I18nProvider"; +import type { TranslationKey } from "../i18n/translations"; import CvTemplateThumbnail from "./CvTemplateThumbnail"; type Props = { @@ -15,8 +16,20 @@ type Props = { disabled?: boolean; }; +const templateLabels = new Map([ + ["modern", ["cvTemplateModernName", "cvTemplateCategoryModern", "cvTemplateModernDescription"]], + ["minimal", ["cvTemplateMinimalName", "cvTemplateCategoryMinimal", "cvTemplateMinimalDescription"]], + ["executive", ["cvTemplateExecutiveName", "cvTemplateCategoryExecutive", "cvTemplateExecutiveDescription"]], + ["technical", ["cvTemplateTechnicalName", "cvTemplateCategoryTechnical", "cvTemplateTechnicalDescription"]], + ["ats-classic", ["cvTemplateAtsClassicName", "cvTemplateCategoryAts", "cvTemplateAtsClassicDescription"]], + ["nordic", ["cvTemplateNordicName", "cvTemplateCategoryModern", "cvTemplateNordicDescription"]], + ["elegant", ["cvTemplateElegantName", "cvTemplateCategoryCreative", "cvTemplateElegantDescription"]], + ["creative", ["cvTemplateCreativeName", "cvTemplateCategoryCreative", "cvTemplateCreativeDescription"]], + ["code", ["cvTemplateCodeName", "cvTemplateCategoryTechnical", "cvTemplateCodeDescription"]], +]); + export default function CvTemplateGallery({ themes, selectedId, onSelect, previewHeight = 170, showDescriptions = true, disabled = false }: Props) { - const { t } = useI18n(); + const { t, language } = useI18n(); const id = useId(); const [atsOnly, setAtsOnly] = useState(true); const visibleThemes = useMemo(() => getVisibleCvThemes(themes, atsOnly, selectedId), [atsOnly, selectedId, themes]); @@ -33,16 +46,18 @@ export default function CvTemplateGallery({ themes, selectedId, onSelect, previe const active = theme.id === selectedId; const locked = theme.available === false; const unavailable = locked || disabled; + const labels = language === "nb" ? templateLabels.get(theme.id) : undefined; + const name = labels ? t(labels[0]) : theme.name; return ( - { if (!unavailable) onSelect(theme); }} onKeyDown={(event) => { if (!unavailable && (event.key === "Enter" || event.key === " ")) { event.preventDefault(); onSelect(theme); } }} sx={{ p: 1, cursor: unavailable ? "not-allowed" : "pointer", position: "relative", minWidth: 0, overflowWrap: "anywhere", borderColor: active ? "primary.main" : "divider", borderWidth: active ? 2 : 1, "&:hover": unavailable ? undefined : { borderColor: "primary.main" } }}> {active && } - {theme.name} - {theme.category} - {showDescriptions && {theme.description}} + {name} + {labels ? t(labels[1]) : theme.category} + {showDescriptions && {labels ? t(labels[2]) : theme.description}} {theme.atsFriendly && } {theme.requiresPro && } diff --git a/job-tracker-ui/src/components/RemindersView.tsx b/job-tracker-ui/src/components/RemindersView.tsx index 3b6c064..4795923 100644 --- a/job-tracker-ui/src/components/RemindersView.tsx +++ b/job-tracker-ui/src/components/RemindersView.tsx @@ -12,6 +12,33 @@ import { useI18n } from "../i18n/I18nProvider"; import { useViewResource, type ViewResourceError } from "../hooks/useViewResource"; import { PageHeader } from "../layout/PageLayout"; import { statusLabel } from "../pipeline"; +import type { TranslationKey } from "../i18n/translations"; + +const generatedReasons = new Map([ + ["Interview stage reached but prep notes are still missing.", "reminderReasonInterview"], + ["Tailored CV and saved application answers still need work.", "reminderReasonCvAndAnswers"], + ["Tailored CV missing for this role.", "reminderReasonCv"], + ["Saved application answers still need work.", "reminderReasonAnswers"], + ["No response yet and no follow-up is scheduled.", "reminderReasonUnscheduled"], + ["Follow-up is due for this role.", "reminderReasonDue"], + ["No urgent workflow gaps are blocking this job right now.", "reminderReasonNoGaps"], +]); +const generatedDayReasons: [RegExp, TranslationKey][] = [ + [/^No reply after (\d+)d$/, "reminderReasonNoReplyDays"], + [/^Stalled after (\d+)d$/, "reminderReasonStalledDays"], + [/^Feedback requested (\d+)d ago$/, "reminderReasonFeedbackDays"], + [/^Waiting (\d+)d$/, "reminderReasonWaitingDays"], +]; + +function reminderReason(reason: string, t: ReturnType["t"]) { + const key = generatedReasons.get(reason); + if (key) return t(key); + for (const [pattern, dayKey] of generatedDayReasons) { + const match = pattern.exec(reason); + if (match) return t(dayKey, { days: match[1] }); + } + return reason; +} type FollowUpFailure = { job: Pick; @@ -62,7 +89,7 @@ function ReminderSection({ title, items, disabled, onOpen, onSetFollowUp }: { ti {j.needsFollowUp ? : null} - {(j.workflowSignal?.reason ?? j.followUpReason) ? : null} + {(j.workflowSignal?.reason ?? j.followUpReason) ? : null} {j.followUpAt ? : null} diff --git a/job-tracker-ui/src/correspondence-inbox-page.test.tsx b/job-tracker-ui/src/correspondence-inbox-page.test.tsx index 8ef30d6..fa43f17 100644 --- a/job-tracker-ui/src/correspondence-inbox-page.test.tsx +++ b/job-tracker-ui/src/correspondence-inbox-page.test.tsx @@ -118,6 +118,24 @@ function renderPage(initialPath = '/') { } describe('CorrespondenceInboxPage', () => { + test('[P7-11 inbox] resumed draft fallback metadata follows locale without mutating stored prose or refetching', async () => { + const original = mockedApi.get.getMockImplementation(); + mockedApi.get.mockImplementation((url: string, config?: any) => { + if (url === '/email/drafts') return Promise.resolve({ data: [savedComposeDraft({ jobApplicationId: 98 })] } as any); + if (url === '/email/providers') return Promise.resolve({ data: [{ provider: 'gmail', displayName: 'Gmail', connected: false, address: null, canRead: false, canSend: false }] } as any); + return original!(url, config); + }); + renderPage(); + fireEvent.click(await screen.findByRole('button', { name: /resume owned compose draft/i })); + fireEvent.change(composeField('emailInboxMessage'), { target: { value: 'My unsaved English message' } }); + const reads = mockedApi.get.mock.calls.length; + await changeLanguage('nb'); + expect(screen.getByText(`${copy('emailInboxJobNumber')} #98 · ${copy('emailInboxSavedDraft')}`)).toBeInTheDocument(); + expect(composeField('emailInboxProvider')).toHaveValue(`Gmail · ${copy('emailInboxReconnectBeforeSending')}`); + expect(composeField('emailInboxMessage')).toHaveValue('My unsaved English message'); + expect(composeField('emailInboxSubject')).toHaveValue('Owned compose draft'); + expect(mockedApi.get).toHaveBeenCalledTimes(reads); expect(mockedApi.put).not.toHaveBeenCalled(); expect(mockedApi.post).not.toHaveBeenCalled(); + }); beforeEach(() => { window.localStorage.clear(); document.documentElement.lang = 'en'; @@ -176,11 +194,12 @@ describe('CorrespondenceInboxPage', () => { renderPage(); expect(await screen.findByRole('heading', { name: /job email/i })).toBeInTheDocument(); - expect(await screen.findByText(/1 items/i)).toBeInTheDocument(); + expect(await screen.findByText("1 item", { exact: true })).toBeInTheDocument(); expect(await screen.findByText(/acme systems/i)).toBeInTheDocument(); expect(await screen.findByText(/backend engineer/i)).toBeInTheDocument(); expect(screen.getByText(/2 labels/i)).toBeInTheDocument(); - expect(screen.getByText(/1 attachments/i)).toBeInTheDocument(); + expect(screen.getByText("1 attachment", { exact: true })).toBeInTheDocument(); + expect(screen.getByText("1 item", { exact: true })).toBeInTheDocument(); expect(screen.getByText(/Gmail: owner@gmail\.test · Read only; reconnect to enable send/i)).toBeInTheDocument(); expect(screen.getByText(/^Outlook: Not connected$/i)).toBeInTheDocument(); expect(screen.queryByText(/Outlook: Not connected.*Read only/i)).not.toBeInTheDocument(); diff --git a/job-tracker-ui/src/cv-template-gallery.test.tsx b/job-tracker-ui/src/cv-template-gallery.test.tsx new file mode 100644 index 0000000..4c120e5 --- /dev/null +++ b/job-tracker-ui/src/cv-template-gallery.test.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import "@testing-library/jest-dom"; +import { act, fireEvent, render, screen } from "@testing-library/react"; +import CvTemplateGallery from "./components/CvTemplateGallery"; +import { I18nProvider } from "./i18n/I18nProvider"; +import { translations } from "./i18n/translations"; +import type { CvTheme } from "./cvBuilder"; + +const theme: CvTheme = { id: "modern", name: "Modern", category: "Modern Professional", description: "Clean SaaS-style layout with a confident accent header.", layout: "header-band", accent: "#2563eb", photoShape: "none", supportsIcons: false, atsFriendly: true, requiresPro: false, available: true, swatches: ["#2563eb"] }; + +beforeEach(() => window.localStorage.clear()); + +test("[P7-11 templates] known metadata follows locale while custom metadata and selection objects stay verbatim", () => { + const select = jest.fn(); + const custom = { ...theme, id: "custom", name: "My Modern", category: "My category", description: "My authored description" }; + render(); + expect(screen.getByRole("button", { name: "Modern" })).toBeInTheDocument(); + act(() => window.dispatchEvent(new StorageEvent("storage", { key: "uiLanguage", newValue: "nb" }))); + expect(screen.getByRole("button", { name: translations.nb.cvTemplateModernName })).toHaveAccessibleDescription(expect.stringContaining(translations.nb.cvTemplateModernDescription)); + expect(screen.getByText(translations.nb.cvTemplateCategoryModern)).toBeInTheDocument(); + expect(screen.getByText(custom.description)).toBeInTheDocument(); + fireEvent.click(screen.getByRole("button", { name: translations.nb.cvTemplateModernName })); + expect(select).toHaveBeenCalledWith(theme); + expect(theme.name).toBe("Modern"); +}); diff --git a/job-tracker-ui/src/reminders-presentation.test.tsx b/job-tracker-ui/src/reminders-presentation.test.tsx index 735a90f..ae6b486 100644 --- a/job-tracker-ui/src/reminders-presentation.test.tsx +++ b/job-tracker-ui/src/reminders-presentation.test.tsx @@ -46,6 +46,21 @@ function signal(actionKey: WorkflowSignal["actionKey"], workspaceTab: WorkflowSi }; } +test("[P7-11 reminders] generated fixed and parameterized reasons follow locale while custom wording stays verbatim", async () => { + mockedApi.get.mockResolvedValue({ data: [ + job(81, { workflowSignal: signal("package-work", "tailored-cv", "Tailored CV missing for this role.") }), + job(82, { workflowSignal: null, followUpReason: "No reply after 14d" }), + job(83, { workflowSignal: null, followUpReason: "No reply after 14d (my own note)" }), + ] } as any); + renderPage(); + await screen.findByText("Tailored CV missing for this role."); + changeLanguage("nb"); + expect(screen.getByText(copy("reminderReasonCv"))).toBeInTheDocument(); + expect(screen.getByText(copy("reminderReasonNoReplyDays", { days: 14 }))).toBeInTheDocument(); + expect(screen.getByText("No reply after 14d (my own note)")).toBeInTheDocument(); + expect(mockedApi.get).toHaveBeenCalledTimes(1); assertNoOtherWrites(); +}); + function job(id: number, overrides: Partial = {}): JobApplication { return { id, jobTitle: `Reminder role ${id}`, company: { id, name: `Company ${id}` }, status: "Waiting", diff --git a/job-tracker-ui/src/views/CorrespondenceInboxPage.tsx b/job-tracker-ui/src/views/CorrespondenceInboxPage.tsx index c12363d..1d3295f 100644 --- a/job-tracker-ui/src/views/CorrespondenceInboxPage.tsx +++ b/job-tracker-ui/src/views/CorrespondenceInboxPage.tsx @@ -463,10 +463,10 @@ export default function CorrespondenceInboxPage() { const item = items.find((candidate) => candidate.jobApplicationId === stored.jobApplicationId); openDraft({ ...stored, - companyName: item?.companyName || `${t("emailInboxJobNumber")} #${stored.jobApplicationId}`, - jobTitle: item?.jobTitle || t("emailInboxSavedDraft"), + companyName: item?.companyName || "", + jobTitle: item?.jobTitle || "", providerName: provider?.displayName || stored.provider, - fromAddress: provider?.address || t("emailInboxReconnectBeforeSending"), + fromAddress: provider?.address || "", }); setSendResult(null); setSendError(null); setDraftError(null); } finally { finishComposeOperation(); } @@ -676,7 +676,7 @@ export default function CorrespondenceInboxPage() { - {view === "inbox" && hasCurrentInbox ? } label={t("emailInboxItems", { count: inboxTotal })} variant="outlined" /> : null} + {view === "inbox" && hasCurrentInbox ? } label={t(inboxTotal === 1 ? "emailInboxItem" : "emailInboxItems", { count: inboxTotal })} variant="outlined" /> : null} {view === "inbox" && hasCurrentInbox ? 0 ? "success" : "default"} /> : null} {view === "inbox" && hasCurrentInbox ? : null} @@ -782,13 +782,13 @@ export default function CorrespondenceInboxPage() { {t("emailInboxDraftTitle")} - {draft.companyName} · {draft.jobTitle} + {draft.companyName || `${t("emailInboxJobNumber")} #${draft.jobApplicationId}`} · {draft.jobTitle || t("emailInboxSavedDraft")} {draftDirty ? : null} - + updateDraft({ to: event.target.value })} disabled={composeBusy || !!sendResult} inputProps={{ maxLength: 320 }} /> updateDraft({ subject: event.target.value })} disabled={composeBusy || !!sendResult} inputProps={{ maxLength: 998 }} /> @@ -839,7 +839,7 @@ export default function CorrespondenceInboxPage() { {item.provider ? : null} {item.externalThreadId ? : } {item.labelCount > 0 ? : null} - {item.attachmentCount > 0 ? : null} + {item.attachmentCount > 0 ? : null} {item.provider === "gmail" && item.externalThreadId ? (