diff --git a/docs/redesign/phase-7/COMMITS.md b/docs/redesign/phase-7/COMMITS.md index 393aa26..3332428 100644 --- a/docs/redesign/phase-7/COMMITS.md +++ b/docs/redesign/phase-7/COMMITS.md @@ -53,4 +53,18 @@ Observed Discovery normal push2026-10-12 22:49:31.085724UTC proves localHEAD=ori | P7-05 Offers | `c723a4e3032185da623d81ace241eec748506fc7` | [Results](p7-05/evidence/offers-results.json):9/1,new4native,backend6,app/browser types/lint/build PASS;14images reviewed/14scans0/15qualified. Hygiene PASS/normal push directly verified2026-10-13 11:19:57UTC, cleanlocalHEAD=origin=live/main unchanged. +7/0frontend696/93/browser267 derived only; Reminders next/main unchanged | -| P7-05 Reminders | Actual hash follows observed normal push | [Results](p7-05/evidence/reminders-results.json):17/3,new6native,backend6,app/browser types/lint/build PASS;17images reviewed/17scans0/114qualified. Final hygiene/normal push pending. +10/+1frontend706/94/browser273 derived only; all4P7-05domains implemented CONDITIONAL GO; P7-06 next/main unchanged | +| P7-05 Reminders | `627955afa989d32fefa53f15da9ef3e0abf27038` | [Results](p7-05/evidence/reminders-results.json):17/3,new6native,backend6,app/browser types/lint/build PASS;17images reviewed/17scans0/114qualified. Hygiene PASS; normal push directly verified2026-10-13 11:49:32.473200UTC, clean localHEAD=origin=live development/main unchanged. +10/+1frontend706/94/browser273 derived only; all4P7-05domains implemented CONDITIONAL GO; P7-06 embedded forms next | + +### P7-06 entry and commit boundaries + +[P7-06 entry](p7-06/evidence/entry.json) records the actual preceding Reminders push. The fresh embedded-forms baseline is39/3 (assets18+overlay18+helper3), executed2026-10-13 11:39:16.918910–11:39:27.061883UTC at HEADc723a4e with uncommitted Reminders; all seven captured relevant sources remained unchanged through the run. This is separate from the later clean627955a push receipt. No P7-06 implementation commit, native acceptance or whole-suite rerun is claimed at entry. + +Use exactly three sequential implementation commits: embedded Application forms, inbox/read review, then compose/send. Optional P7-06F remains DEFERRED until mode→draft/review action and fallback have an explicit product decision. Existing compose identity/save/delivery findings remain a prerequisite at the third boundary. + +### P7-06 forms candidate gate — no implementation hash assigned + +[Bounded forms report](p7-06/FORMS.md): first embedded-forms slice CONDITIONAL GO pending final static/push; overallP7-06 ongoing. Latest54/4 units/backend30 no-build pass. Current buildxyh9FYyySEw1uIao4DveJ native8/1 retains only unchanged strict language-save204→HTTP500 EN (RD11); keyboard PageDown passes. Fresh current Jobs5/P2-D01ordinary1 give14pass/1fail of15; previous-build CV4/Career11 give29pass/1fail relevant30 with as-run qualifiers. No rerun/waiver, new axe total, commit hash or push receipt is invented. Ten new frontend cases/zero suites derive716/94 and nine native cases derive282 cases only. Final static/hygiene/source-export/push evidence is pending. Keep the three approved sequential boundaries and production NO-GO. + +### Final P7-06 embedded-forms observation + +Final embedded-forms implemented scope is **CONDITIONAL GO**, normal push pending. The matching aggregate/generation repair resolves the reviewed cross-job draft leak: ordinary parent RED18/1, latest affected **55/4 PASS** and actual SPA/heldB/exact two-job readback PASS. Final parent build9sM6acEkOGxgN5fTRZXdE has **9PASS/1FAIL of10** (unchanged strict actual NB language204→500/RD11); fresh Jobs5/P2-D01ordinary1 give **15PASS/1FAIL of16**, qualified earlier CV4/Career11 give **30PASS/1FAIL relevant31**. Backend30 sameDLL/no-build; app/browser types/lint/build PASS. All23/111 current forms PNGs actually reviewed, 55 recorded axe scans0/741 (current22/154+Jobs8/89; earlierCV12/121+Career13/377), full incomplete contexts pending human qualification. See [portable forms result](p7-06/evidence/forms-results.json). +11frontend/0suites derives **717/94**; +10browser derives **283**, no integrated run. All57 groups56PARTIAL/C11DEFERRED and32routes, P513withheld/06Fdeferred/main unchanged/productionNO-GO. Inbox/read and compose/send remain; Phase8 not started. diff --git a/docs/redesign/phase-7/DEBT.md b/docs/redesign/phase-7/DEBT.md index 64d850f..4dfbaac 100644 --- a/docs/redesign/phase-7/DEBT.md +++ b/docs/redesign/phase-7/DEBT.md @@ -154,3 +154,19 @@ Read-only connected-code inspection confirms existing CorrespondenceInboxPage up ### P7-05 Reminders finite evidence [Reminders results](p7-05/evidence/reminders-results.json) repair reproduced duplicateheldPATCHs, toast-only local failure, host-locale dates, localeGET and managedfocus/reflow in the existing owner. Old GET-only retry/movedfocus behaviors already passed RED and remain. Explicit captured write Retry does not prove network-uncertain event deduplication; lastwriter/date/DST obligations remain. Clear shows existing Shellpoll lag; generated English reasons inNB go toP7-11, customfallback staysraw. New17all-enabledscans0/114 retain full contrast/heading-context requirements; no human/device/output/wholePRES debt closes. + +The normal Reminders627955a push was directly verified2026-10-13 11:49:32.473200UTC with clean localHEAD=origin=live development and unchanged local/origin/live main27657f3. This closes only the predecessor push status; the finite debt above remains. + +### P7-06 embedded-forms entry dependencies + +[Entry evidence](p7-06/evidence/entry.json) records the fresh39/3 pre-change affected baseline atc723a4e with uncommitted Reminders; it is not a clean627955a baseline. Read-only connected-code review identifies mutation errors hiding loaded dirty controls/history, missing initial GET Retry, locale-triggered reads, dirty Restore without confirmation and AI Apply during parent Save losing edits while a mutable action can misattribute the suggestion. Meaningful RED, synchronous captured-operation guards and finite native recovery/preservation proof are required before first-slice acceptance. No repair or runtime acceptance is claimed here. Bodyless restore/noExpectedVersion and RD21 remain preserved contract/debt, not hidden concurrency repair. Keep the seven-section workspace/table/renderer and the later compose dependency above; optional06F remains DEFERRED pending its action/fallback decision. + +### P7-06 forms candidate and retained failures + +[Forms report](p7-06/FORMS.md) records meaningful initial18pass/8fail RED, the scoped ambiguous old error locator, two GREEN-only identity additions, actual visual creation/attachment feedback defect and strengthened48pass/1fail RED before latest54/4 GREEN. Repairs keep loaded drafts/history and explicit read/mutation recovery, synchronous captured Save/AI/Restore ownership and keyed job disposal. Existing CV duplicate association semantics require explicit attachment recovery using the same created ID; no transaction/distributed-idempotency claim. Latest region/H3/Tooltip semantics have current native PageDown proof. First forms slice CONDITIONAL GO pending final static/push; overallP7-06 ongoing. + +The real native language save produced HTTP500 on repeated locale attempts with the unchanged DLL; earlier9/9 does not close RD11. Latest17:14:27–17:16:29UTC native8/1 retains only the strict real-account EN language-save204 expectation receiving500; no rerun or waiver. Fresh current Jobs5/P2-D01ordinary1 give14pass/1fail of15; previous-build CV4/Career11 extend to29pass/1fail relevant30 with as-run qualifiers. The separate controlled503 case only proves local draft/Toast/exact-language Retry with unchanged account language; original real204 assertions remain. Keep RD11 diagnosis at P7-09. Prior55 scans0/749 full vectors and22/110 actual earlier image inspections remain qualified; no new axe totals are inferred. Generated Tailoring English titles/details in NB and raw history source/action enums go to P7-11; user text stays verbatim. RD21, translation-dependent frame reload, attachment ID0, AI-text freshness, human/device/zoom/fonts/photo/print/provider/release conditions and later compose dependency remain; no whole PRES status or production gate closes. + +### Final P7-06 embedded-forms observation + +Final embedded-forms implemented scope is **CONDITIONAL GO**, normal push pending. The matching aggregate/generation repair resolves the reviewed cross-job draft leak: ordinary parent RED18/1, latest affected **55/4 PASS** and actual SPA/heldB/exact two-job readback PASS. Final parent build9sM6acEkOGxgN5fTRZXdE has **9PASS/1FAIL of10** (unchanged strict actual NB language204→500/RD11); fresh Jobs5/P2-D01ordinary1 give **15PASS/1FAIL of16**, qualified earlier CV4/Career11 give **30PASS/1FAIL relevant31**. Backend30 sameDLL/no-build; app/browser types/lint/build PASS. All23/111 current forms PNGs actually reviewed, 55 recorded axe scans0/741 (current22/154+Jobs8/89; earlierCV12/121+Career13/377), full incomplete contexts pending human qualification. See [portable forms result](p7-06/evidence/forms-results.json). +11frontend/0suites derives **717/94**; +10browser derives **283**, no integrated run. All57 groups56PARTIAL/C11DEFERRED and32routes, P513withheld/06Fdeferred/main unchanged/productionNO-GO. Inbox/read and compose/send remain; Phase8 not started. diff --git a/docs/redesign/phase-7/PRESERVATION.md b/docs/redesign/phase-7/PRESERVATION.md index 03e5356..64824c0 100644 --- a/docs/redesign/phase-7/PRESERVATION.md +++ b/docs/redesign/phase-7/PRESERVATION.md @@ -167,4 +167,16 @@ Implemented P7-03 scope is **CONDITIONAL GO**. The normal import/history push `5 ### P7-05 Reminders and checkpoint disposition -[Reminders results](p7-05/evidence/reminders-results.json): J01/J02/J09/W11 unchanged groups/reasons/query/date/body/event/route and owner boundaries; capturedwrite versusGET-only retry, held single mutation and focus. Fresh17/3/new6native/backend6/types/lint/build pass;17actual image reviews/17all-enabledscans0/114 qualified. All57 remain56 PARTIAL/C11 DEFERRED/32routes. Generated reason localisation→P7-11; polling/network-uncertain write/DST/manual obligations retained. P7-05 implemented CONDITIONAL GO/hygienePASS+pushpending;706/94/browser273 derived only. P7-06 entry must preserve loaded dirty forms/frozen history and repair confirmed compose identity/save races at its approved boundary. +[Reminders results](p7-05/evidence/reminders-results.json): J01/J02/J09/W11 unchanged groups/reasons/query/date/body/event/route and owner boundaries; capturedwrite versusGET-only retry, held single mutation and focus. Fresh17/3/new6native/backend6/types/lint/build pass;17actual image reviews/17all-enabledscans0/114 qualified. All57 remain56 PARTIAL/C11 DEFERRED/32routes. Generated reason localisation→P7-11; polling/network-uncertain write/DST/manual obligations retained. P7-05 implemented CONDITIONAL GO/hygienePASS; normal627955a push directly verified2026-10-13 11:49:32.473200UTC, clean localHEAD=origin=live/main unchanged.706/94/browser273 remain derived only. + +### P7-06 embedded-forms entry + +[Entry evidence](p7-06/evidence/entry.json) retains fresh39/3 affected baseline atc723a4e with uncommitted Reminders and seven unchanged captured sources. W01/W05–W10 retain the seven-section workspace, one CV variant, letter source/action and additive owned history, bodyless restore without ExpectedVersion (RD21), the two-answer Notes encoding/clear behavior and frozen package bytes. Loaded dirty controls/history, initial read Retry, locale-only no-read behavior, dirty Restore confirmation and captured/synchronous Save–AI review protection are first-slice acceptance requirements, not established fixes. M01–M06 inbox/provider/draft/send contracts remain for the next two sequential boundaries; known compose identity/save races must be repaired there. All57 statuses/32routes and manual/provider/output/release conditions remain unchanged; no P7-06 runtime acceptance is claimed at entry. + +### P7-06 embedded forms — CONDITIONAL GO, final static/push pending + +[Forms report](p7-06/FORMS.md) records loaded-draft/history recovery, five GET Retry owners without locale asset reads, dirty Restore confirmation, captured Save/AI action and keyed job-identity/unmount guards. Explicit attachment recovery reuses the created CV ID without another duplicate; the existing duplicate endpoint initially retains both association IDs, so no atomicity/idempotency claim is made. W01/W05–W08 package/Notes/letter/history contracts and bodyless restore/noExpectedVersion remain. Latest54/4 units/backend30 no-build pass; current buildxyh9FYyySEw1uIao4DveJ native8/1 retains only strict language-save204→HTTP500 EN (RD11), with PageDown passing. Current Jobs5/P2-D01ordinary1 give14pass/1fail of15; previous-build CV4/Career11 extend to29pass/1fail relevant30 with as-run qualifiers/actual prior PDF200. First forms slice CONDITIONAL GO pending final static/push; overallP7-06 ongoing. Prior55 scans0/749 and actual22/110 earlier image inspections remain qualified; no latest axe total or push is inferred. Derived716/94 frontend/282browser cases do not establish integrated totals; all57 statuses/32routes, manual/provider/output/production gates remain unchanged. + +### Final P7-06 embedded-forms observation + +Final embedded-forms implemented scope is **CONDITIONAL GO**, normal push pending. The matching aggregate/generation repair resolves the reviewed cross-job draft leak: ordinary parent RED18/1, latest affected **55/4 PASS** and actual SPA/heldB/exact two-job readback PASS. Final parent build9sM6acEkOGxgN5fTRZXdE has **9PASS/1FAIL of10** (unchanged strict actual NB language204→500/RD11); fresh Jobs5/P2-D01ordinary1 give **15PASS/1FAIL of16**, qualified earlier CV4/Career11 give **30PASS/1FAIL relevant31**. Backend30 sameDLL/no-build; app/browser types/lint/build PASS. All23/111 current forms PNGs actually reviewed, 55 recorded axe scans0/741 (current22/154+Jobs8/89; earlierCV12/121+Career13/377), full incomplete contexts pending human qualification. See [portable forms result](p7-06/evidence/forms-results.json). +11frontend/0suites derives **717/94**; +10browser derives **283**, no integrated run. All57 groups56PARTIAL/C11DEFERRED and32routes, P513withheld/06Fdeferred/main unchanged/productionNO-GO. Inbox/read and compose/send remain; Phase8 not started. diff --git a/docs/redesign/phase-7/README.md b/docs/redesign/phase-7/README.md index 9b3346e..ef83d0a 100644 --- a/docs/redesign/phase-7/README.md +++ b/docs/redesign/phase-7/README.md @@ -13,8 +13,8 @@ The entry commit is `b5319981ad3d1bd635daff861453d23c0de5320b` on `codex/ui-rede | [P7-02](p7-02/README.md) | Onboarding and Career overview/evidence | Entryad7b419 pushed/verified; bounded onboarding CONDITIONAL GO32/13/4+types/lint/build/13 actual PNG inspections, hygiene PASS and d21501aa push verified23:55:12UTC. Separate SQLite evidence-list repair91 guards/HTTP1/Release passed, pushed01281f52/direct receipt00:06:47UTC with unfinished Career work preserved. Career affected46/3/native14/preservation2/types/lint/build and actual22-image review pass; implemented checkpoint CONDITIONAL GO with99 incompletes/manual gates. Career final hygiene PASS (four commands0,9docs347links0errors); normally pushed8b4d5c2/direct localHEAD=origin=live receipt05:51:04UTC, clean worktree/main unchanged | | [P7-03](p7-03/README.md) | Career structured editing and import review | Source-ID and structured-editor commits pushed; final import/history runtime/design CONDITIONAL GO: affected76/6, native35, backend98/seeder24, types/lint/build PASS;33/54 final images inspected. Import17 scans0/302, editor13 scans0/377, Career22 scans0/99 retained. Final staged hygiene PASS; normal push5d6e3235 directly verified17:28:54UTC with clean worktree/main unchanged. RD21/human/provider/output gates remain;57/32 unchanged | | [P7-04](p7-04/README.md) | CV library and public entry | Implemented CONDITIONAL GO; library fcacae7 push verified. Public affected38/3/native8/types/lint/build and fresh fidelity16/36+8/24 PASS;18 images actually reviewed, frame-tested12/manual gates retained. Public final hygiene PASS/normal push bfa0ab6 directly verified22:24:44UTC, clean main unchanged | -| [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/Reminders normal push pending; P7-06 next | -| P7-06 | Remaining Application forms and global correspondence | Existing seven-section workspace, immutable packages and draft/send owners; planned | +| [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 | Overall ongoing; [embedded forms](p7-06/FORMS.md) CONDITIONAL GO pending final static/push. Entry39/3/latest54/4 units/backend30 no-build retained. Current native8/1 has only unchanged strict language-save204→HTTP500 EN (RD11); current Jobs5/P2-D01ordinary1 give14pass/1fail of15. Previous-build CV4/Career11 give29pass/1fail relevant30 with as-run qualifiers. PageDown passes; no rerun/waiver or new axe/push claim. Derived716/94 frontend/282browser cases only;57/32 unchanged. Inbox/read review then compose/send remain separate sequential commits | | P7-06F | Deferred `followMode` reviewed composer | Optional separately reviewed mail slice; **DEFERRED** until mode→draft/review action and fallback are explicitly decided | | P7-07 | Documents owner-scoped index | Authenticated metadata-only paginated backend projection first; source destinations from P7-04/06; planned | | P7-08 | Existing notifications and operations | Existing store and real destinations; new enabled producers require F20 recovery evidence; planned | @@ -56,3 +56,7 @@ Implemented P7-03 scope is **CONDITIONAL GO**. The normal import/history push `5 ### P7-04 library finite disposition [Library results](p7-04/evidence/library-results.json) record fresh affected38/4 (+12/0), ordinary native8, existing editor/gallery consumer4 with actual PDF200, unchanged-binary backend72 and types/lint/build PASS on _fYf-IvM_ULd3ZPdUW-8h. All19 final PNGs actually reviewed; finite root overflow0. Library19 all-enabled scans0/64 and editor12 retained-scope scans0/122 remain contextually qualified: badge/overlap/modal portals, disabled Undo/Redo wrappers and obscured print headings retain M09/M11/manual requirements. Server template names/categories remain English metadata in NB and are P7-11 localisation residual. Meaningful unit/native RED and peer fixes preserve draft/error/current-locale/managed-focus behavior; no assertion/rule waiver. Implemented library CONDITIONAL GO with final hygiene/push pending; public entry/full P7-04 pending. All57 statuses/32 routes unchanged; frontend660/93/browser241/backend1,034 derived only, full573/89/backend1,033/sidecar49 inherited until P7-12. RD21/canonical concurrency, old orphan/frozen/output/provider/human/release debt remains; P5-13 withheld/main NO-GO/Phase8 separate. + +### Final P7-06 embedded-forms observation + +Final embedded-forms implemented scope is **CONDITIONAL GO**, normal push pending. The matching aggregate/generation repair resolves the reviewed cross-job draft leak: ordinary parent RED18/1, latest affected **55/4 PASS** and actual SPA/heldB/exact two-job readback PASS. Final parent build9sM6acEkOGxgN5fTRZXdE has **9PASS/1FAIL of10** (unchanged strict actual NB language204→500/RD11); fresh Jobs5/P2-D01ordinary1 give **15PASS/1FAIL of16**, qualified earlier CV4/Career11 give **30PASS/1FAIL relevant31**. Backend30 sameDLL/no-build; app/browser types/lint/build PASS. All23/111 current forms PNGs actually reviewed, 55 recorded axe scans0/741 (current22/154+Jobs8/89; earlierCV12/121+Career13/377), full incomplete contexts pending human qualification. See [portable forms result](p7-06/evidence/forms-results.json). +11frontend/0suites derives **717/94**; +10browser derives **283**, no integrated run. All57 groups56PARTIAL/C11DEFERRED and32routes, P513withheld/06Fdeferred/main unchanged/productionNO-GO. Inbox/read and compose/send remain; Phase8 not started. diff --git a/docs/redesign/phase-7/p7-05/README.md b/docs/redesign/phase-7/p7-05/README.md index c0d87c4..590b6e8 100644 --- a/docs/redesign/phase-7/p7-05/README.md +++ b/docs/redesign/phase-7/p7-05/README.md @@ -1,6 +1,6 @@ # P7-05 — Discovery, Companies, Offers and Reminders -**P7-05 implemented scope CONDITIONAL GO; all four domains implemented, final Reminders hygiene PASS/normal push pending.** Follow the exact [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries): Discovery, Companies, Offers, then Reminders as four separate normal development commits. +**P7-05 implemented scope CONDITIONAL GO; all four domains implemented, final Reminders hygiene PASS/normal push verified.** Follow the exact [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries): Discovery, Companies, Offers, then Reminders as four separate normal development commits. [Entry evidence](evidence/entry.json) records the actual bfa0ab6 public push, clean direct development equality/main unchanged and13 exact committed source baselines. P7-04 implemented scope is CONDITIONAL GO: affected38/3/public native8/fidelity16/36+8/24/types/lint/build/static checks pass;18 public PNGs actually inspected. Sandbox frame-tested12 and separate settings incompletes223 remain. Whole frontend573/89/backend1,033/sidecar49 are inherited; frontend673/93/browser249/backend1,034 are derived expectations, not integrated reruns. @@ -65,7 +65,7 @@ Unit RED1/8 becomes9/1; a test-only narrow Intl.Locale mock type was corrected w ## Reminders implemented result and P7-05 disposition -**Reminders and implemented P7-05 scope CONDITIONAL GO; hygiene PASS/normal push pending.** [Reminders entry](evidence/reminders-entry.json) carries the actual verified Offers pushc723a4e. [Finite results](evidence/reminders-results.json): fresh **17/3 frontend**, **6 ordinary native**, **6 unchanged-backend**, app/browser TypeScript, lint and build pass. New10/+1suite gives **706/94 frontend derived only**; new6 gives **273 browser derived only**. P7-05 adds33 cases/1suite and24 ordinary browser cases across four domains; no integrated totals until P7-12. Latest whole573/89/backend1,033/sidecar49 stay inherited, backend1,034 derived. +**Reminders and implemented P7-05 scope CONDITIONAL GO; hygiene PASS/normal push627955a verified.** [Reminders entry](evidence/reminders-entry.json) carries the actual verified Offers pushc723a4e. [Finite results](evidence/reminders-results.json): fresh **17/3 frontend**, **6 ordinary native**, **6 unchanged-backend**, app/browser TypeScript, lint and build pass. New10/+1suite gives **706/94 frontend derived only**; new6 gives **273 browser derived only**. P7-05 adds33 cases/1suite and24 ordinary browser cases across four domains; no integrated totals until P7-12. Latest whole573/89/backend1,033/sidecar49 stay inherited, backend1,034 derived. Existing four groups, server reason precedence, group order,14-day query and workflow destinations remain. Shared H2/header, named stable region, H3 groups with readable counts, H4 named articles, tokens, wrapping chips and actions replace one-off presentation. Canonical statuses and dates follow current UI language; translated controls include their visible text in contextual accessible names. Custom fallback text remains verbatim; generated serverEnglish reasons in Bokmål are explicit P7-11 residual localisation work. @@ -79,4 +79,4 @@ Meaningful RED2/8 includes3 actual PATCHs for each held+3/+7/clear, unreachable All four approved P7-05 domains are now implemented as separate coherent commits: Discovery34a4d6d, Companiesa8aba85, Offersc723a4e and this Reminders boundary. Proceed to P7-06 embedded forms, then inbox/read, then compose/send. Existing seven-section workspace remains; confirmed recovery/dirty/delivery safety findings are entry dependencies for the affected owner, not permission for a framework or schema rewrite. -[Final finite hygiene](evidence/reminders-static-checks.json) passed four raw commands at2026-10-13 11:48:26–28UTC,12documents/437links0errors,57groups/32routes. Final staged whitespace follows staging; normal Reminders push receipt follows observation in P7-06 entry. +[Final finite hygiene](evidence/reminders-static-checks.json) passed four raw commands at2026-10-13 11:48:26–28UTC,12documents/437links0errors,57groups/32routes. The normal Reminders push `627955afa989d32fefa53f15da9ef3e0abf27038` was directly verified at2026-10-13 11:49:32.473200UTC: localHEAD=origin=live development, clean worktree, local/origin/live main unchanged `27657f31131cd95d42f0e1290853ec9c9b3959a4`. The actual receipt is retained in finite results and [P7-06 entry](../p7-06/evidence/entry.json); no new runtime or human acceptance follows from the push. diff --git a/docs/redesign/phase-7/p7-05/evidence/reminders-results.json b/docs/redesign/phase-7/p7-05/evidence/reminders-results.json index e56bc3c..67d5947 100644 --- a/docs/redesign/phase-7/p7-05/evidence/reminders-results.json +++ b/docs/redesign/phase-7/p7-05/evidence/reminders-results.json @@ -3,7 +3,7 @@ "checkpoint": "P7-05", "slice": "Reminders", "recordedUtc": "2026-10-13T11:48:25.399199+00:00", - "acceptance": "REMINDERS_AND_P7_05_IMPLEMENTED_SCOPE_CONDITIONAL_GO_HYGIENE_PASS_NORMAL_PUSH_PENDING", + "acceptance": "REMINDERS_AND_P7_05_IMPLEMENTED_SCOPE_CONDITIONAL_GO_HYGIENE_PASS_NORMAL_PUSH_VERIFIED", "entryReceipt": { "checkpoint": "P7-05", "slice": "Reminders", @@ -2192,7 +2192,7 @@ "freshIntegratedAcceptance": "P7-12 pending" }, "preservation": "All57 remain56 PARTIAL/C11 DEFERRED;32 routes. J01/J02/J09/W11 four groups, owner read/write, captured-date/error/read recovery and context/focus supported; no query/calendar/schema/API/history meaning/model/renderer/production change.", - "remaining": "P7-06 onward pending; confirmed embeddedforms/compose safety findings must be repaired at their approved boundaries. Human AT/device/realzoom/fontphoto/provider/release obligations remain;P5-13 withheld/P7-06Fdeferred/Phase8separate/mainunchanged/productionNO-GO.", + "remaining": "P7-06 entry recorded; embedded forms ongoing, then separate inbox/read and compose/send boundaries. No P7-06 runtime acceptance claimed by this predecessor report. Confirmed embeddedforms/compose safety findings must be repaired at their approved boundaries. Human AT/device/realzoom/fontphoto/provider/release obligations remain;P5-13 withheld/P7-06Fdeferred/Phase8separate/mainunchanged/productionNO-GO.", "staticChecks": { "schemaVersion": 1, "checkpoint": "p7-05", @@ -2297,5 +2297,105 @@ "Static evidence is not runtime/manual/device/provider/production acceptance." ] }, - "peerReview": "Bounded read-only connected-contract review found no material introduced issue; local mutex is not distributed idempotency." + "peerReview": "Bounded read-only connected-contract review found no material introduced issue; local mutex is not distributed idempotency.", + "push": { + "observedUtc": "2026-10-13T11:49:32.473200+00:00", + "branch": "codex/ui-redesign-phase4", + "head": "627955afa989d32fefa53f15da9ef3e0abf27038", + "originDevelopment": "627955afa989d32fefa53f15da9ef3e0abf27038", + "liveDevelopment": "627955afa989d32fefa53f15da9ef3e0abf27038", + "main": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "originMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "liveMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "workingTreeClean": true, + "pendingPaths": [], + "commands": [ + { + "command": [ + "rtk", + "proxy", + "git", + "branch", + "--show-current" + ], + "exitCode": 0, + "stdout": "codex/ui-redesign-phase4\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "HEAD" + ], + "exitCode": 0, + "stdout": "627955afa989d32fefa53f15da9ef3e0abf27038\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/codex/ui-redesign-phase4" + ], + "exitCode": 0, + "stdout": "627955afa989d32fefa53f15da9ef3e0abf27038\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\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": "627955afa989d32fefa53f15da9ef3e0abf27038\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": "", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + } + ] + } } diff --git a/docs/redesign/phase-7/p7-06/FORMS.md b/docs/redesign/phase-7/p7-06/FORMS.md new file mode 100644 index 0000000..ac4ad76 --- /dev/null +++ b/docs/redesign/phase-7/p7-06/FORMS.md @@ -0,0 +1,81 @@ +# P7-06 — Embedded Application forms + +**Embedded forms: CONDITIONAL GO; normal development push pending.** Latest affected55/4 PASS, native9/1 and15/1 final-build consumers retain the strict real-account language-save HTTP500 failure (RD11); it is not waived or rerun to replace the result. Overall P7-06 remains ongoing. This is the first of exactly three sequential P7-06 commits: embedded forms → inbox/read review → compose/send. No implementation hash or push receipt is assigned here. Optional P7-06F remains DEFERRED until its action/fallback decision exists. + +The [entry](evidence/entry.json) retains the actual627955a Reminders receipt and the earlier fresh39/3 baseline atc723a4e with uncommitted Reminders work. Seven relevant captured sources remained unchanged through that baseline. The clean receipt and pre-change run are separate observations. + +## Bounded repairs and preserved behavior + +The existing `ApplicationAssets.tsx` owner retains loaded controls, dirty drafts and owned history after failed mutations. Its five initial reads expose explicit GET Retry; changing UI language retranslates without another asset GET or write. Archive Retry repeats the captured archive read; failed mutations remain deliberate original actions rather than an automatic replay. + +Dirty Restore uses the shared confirmation and a synchronous mutation mutex; Cancel makes zero restore POSTs. AI Apply stays blocked while the parent Save is busy, and suggestion source/action is captured so changing the current action cannot relabel an earlier result. Five job-ID keyed children and unmount guards dispose old-job state and prevent old completions from copying into a new job, redirecting or invoking its saved callback. + +The existing CV duplicate endpoint preserves `JobApplicationId` on both variants before explicit attachment. Consequently, a newer GET projection can differ from stale UI association state. When creation succeeds but attachment fails, the candidate retains that exact created ID and offers explicit attachment recovery, normalizing to one associated variant without another duplicate. This is finite local recovery, not transactionality or distributed idempotency. Actual image034 exposed misleading “could not create” copy after successful creation; the feedback now distinguishes attachment failure. + +The shared RichText Tooltip is hidden only when Popper marks its reference hidden. Application forms, CV Editor and Career remain its three consumers. Final dialog work uses `DialogContent` as a named region with `tabIndex=0`, H3 subheads and both Restore Tooltips with `describeChild`, replacing the owner generic-label semantics. Latest native evidence proves keyboard PageDown scrolling. Updated scan totals are not inferred from this source change. + +Retain the seven-section workspace, table, Router/history/aliases and renderer. One CV variant, letter source/action and additive owned history, bodyless restore/noExpectedVersion, the two-answer Notes encoding/clear behavior, P2-D01 frozen package bytes and archived binaries remain unchanged. No backend or schema contract was changed; RD21 last-writer/partial-write limits remain. + +## Unit evidence and count provenance + +| Observation | Executed outcome | Qualification | +|---|---|---| +| Pre-change baseline,11:39:16–27UTC |39/3: assets18+overlay18+helper3; no failed/pending/todo | Exact frozen entry capsule; not a clean627955a baseline | +| Eight strengthened cases,12:07:57–12:08:04UTC |Assets26:18 pass/8 fail | Meaningful RED for the existing defects; raw command/report retained in `forms/unit-red` | +| First candidate |46/47, then47/3 after scoping an old assertion | The global error locator became ambiguous because the AI read failure is now visible; the assertion retains its original intent rather than hiding the error | +| Two keyed-identity cases |49/3: assets28+overlay18+helper3 | GREEN-only additions; do not claim those two reproduced RED | +| Creation/attachment copy correction,16:56:29–48UTC |48 pass/1 fail | Strengthened existing assertion after actual visual review; raw prefix `p706-forms-copy-attachment-red-20261013-review-20261013T165629Z` | +| Corrected copy plus named controls |54/4 | Raw GREEN prefix `p706-forms-copy-attachment-green-20261013-20261013T170047Z` | +| Latest semantic candidate,17:09:04.624961–17:09:20.681746UTC |54/4: affected49/3 plus existing named controls5/1; zero failed/pending/todo | Raw prefix `p706-forms-final-semantics-green-20261013-20261013T170904Z`; no complete slice acceptance | + +Raw prefixes above are under `.artifacts/phase7/current/unit`; each retains its JSON report, command and log. Ten new frontend cases/zero new suites derive **716/94**, not a fresh full frontend run. The five named-control cases already belong to the inherited whole suite and are not counted again. Nine new ordinary native cases (eight original plus a separate controlled503 case) derive **282 browser cases**, not an integrated total or282 fresh passes. Latest full573/89 frontend, backend1,033 and sidecar49 remain inherited; backend1,034 remains derived only. + +## Native, backend and consumer evidence + +| Run | Outcome | Retained limit | +|---|---|---| +| Native attempt1 |1 pass/7 fail | Exact failing traces/source/export in `.artifacts/phase7/p7-06/forms/attempt-1-F7dRaRLWo_pjz6zoPoAPk` | +| Native attempt2 |6 pass/2 fail | Exact capsule `attempt-2-_Hza-JrlN1-7iFYcv7mKU`; failures remain recorded | +| Native attempt3,16:48:57.993686–16:50:58.615845UTC |9/9 pass | Earlier candidate capsule `.artifacts/phase7/p7-06/forms/final/results.json`, SHA256 `4da6523e909f0a69cfead47e73cd094bb391b37d181e7efd05b5421919f6e322`; does not validate later semantic changes | +| Native attempt4,17:02:34–17:04:33UTC |8 pass/1 fail | Known real language-save HTTP500; `attempt-4-7rHcRydumjgHBrGRn7NZA/results.json`, SHA256 `5268cfd446ba5b91917c2ccc30c786584eb8d497278373f397a2e6d2c0501de1` | +| Latest semantic native,17:14:27.950279–17:16:29.233143UTC |8 pass/1 fail of9 | Build `xyh9FYyySEw1uIao4DveJ`; only unchanged strict real-account language-save204 expectation receives HTTP500 on EN (RD11). Keyboard PageDown scrolling passes; no rerun/waiver | +| Fresh current Jobs,17:19:43.987302–17:21:24.862279UTC |5 pass | Same current build; retained seven-section workspace consumer proof | +| Fresh current P2-D01,17:21:24.868267–17:21:36.805313UTC |1 ordinary pass | Same current build; native frozen-document preservation remains ordinary | +| Earlier RichText/workspace consumers,16:41:46–16:45:50UTC |Jobs5/P2-D01 ordinary1/CV4/Career11 pass | Previous build/as-run source evidence; actual CV PDF200 and unchanged RichText bytes through those lanes. CV4/Career11 are not relabelled as fresh current-build runs | +| Existing backend asset/package subset,16:18:00.184066–16:18:03.191603UTC |30/30, no-build | Same verified unchanged Release DLL; raw `.artifacts/phase7/current/backend-career/p706-forms-20261013T161800Z/command.json` and `career.trx`. No backend rebuild/full rerun | + +Fresh final current-build evidence is **14 pass/1 fail of15** (forms8/1+Jobs5+P2-D01ordinary1). Including previous-build CV4 and Career11 gives **29 pass/1 fail of30 relevant cases**, with those build/source qualifiers retained. Repeated earlier Jobs/P2-D01 runs are not added again. Latest actual frontend remains54/4 at17:09UTC; these are finite affected/consumer counts, not whole-suite acceptance. + +All native cases remain ordinary: no skip, expected failure, weakened real204 assertion, automatic retry or disabled rule is used. Actual HTTP500 occurred on repeated locale attempts with the unchanged DLL; the latest sole failure is EN. A passing run does not close RD11; retain diagnosis at P7-09. The separate controlled503 case proves only local dirty-draft retention, Toast feedback, exact language Retry and unchanged account language. It does not repair or replace the original real204 test. + +The latest dialog semantics now have current-build native PageDown and workspace/frozen-document evidence, qualified by the retained RD11 failure. Final static/hygiene and current source/export evidence must be finalized before the normal development commit/push; no push hash or receipt is claimed here. The first forms slice is CONDITIONAL GO on that finite scope, while complete workflow human/device/provider/output and production acceptance remain unavailable. + +## Visual/accessibility evidence and retained debt + +The operator actually displayed/reviewed **all22 current forms PNGs** from `.artifacts/phase7/p7-06/forms/final-verified`:22/110 total images, with88 other retained images. Current creation-success/attachment-failure feedback now correctly describes the attachment failure. The earlier22/110 review that exposed the incorrect copy remains a separate observation; repeated images are not additional cases. Full-page sticky composites and the long360×5288 image scaled to139×2048 do not prove every pixel, viewport occlusion, real zoom or a physical device. Textarea autosizing remains; no short screenshot is substituted for long-form evidence. + +Final verified capsule `.artifacts/phase7/p7-06/forms/final-verified/results.json` has SHA256 `28da994d574e819208d65edc756fac13eca0597a9d622bcd47a3eb488db7eea4`; captured `ApplicationAssets.tsx` has SHA256 `d3be34c85fd4ab8984356f5fdc8a5b741590120e4253f97246bf870486d027c1`. These identify the current reviewed evidence/source; final portable/static records and normal push observation remain separately pending. + +Prior triage contains **55 scans,0 violations/749 incompletes**: new22 default+target-size0/162; Jobs8 WCAG0/89; CV12 WCAG0/121; Career13 default+target-size0/377. Exact vectors remain in `.artifacts/phase7/p7-06/forms/axe-triage-prior.json`, SHA256 `f24695b6deaa30798672f4c0485fb68fa536877da14fa1c4006df1fddf7f279a`. Contrast623 includes unknown ratio0 contexts and526 overlap-labelled contexts with empty related nodes; span-label, hidden-modal and frame-tested contexts retain their own conditions. Empty related nodes/ratio0 do not establish a cause, contrast ratio or AT acceptance. + +Latest contextual triage is `.artifacts/phase7/p7-06/forms/axe-triage-final.json`, referenced by the [portable result](evidence/forms-results.json). The current forms **21 scans0/147** and Jobs **8 scans0/89** join earlier qualified CV12/121 and Career13/377, totaling **54 scans0/734**. Four Restore span labels and three DialogContent generic labels disappear across matched scans. The remaining eight-node reduction comes from the missing second real-locale capture after HTTP500, not improved contrast. Forty-two other generic span labels remain; modal/background, sandbox-frame and unknown contrast contexts still require manual qualification. No rule was disabled and no incomplete was treated as a pass. + +The portable result verifies453 saved file references, all six pre-native sources,74 current export files and the unchanged backend DLL. A failed collector attempt with incorrectly named source paths remains an incomplete ignored `final-semantic` folder; it performed no tests and is not an acceptance capsule. `final-verified` is authoritative for this final observation. Current [static checks](evidence/forms-static-checks.json) and the separately observed successor push receipt complete the bounded evidence record. + +## Parent aggregate identity repair after review + +Read-only review found that the keyed answers child could still receive A's aggregate while navigating to B. This inherited parent gap allowed editing one B field to retain and later save A's other field. The original child-only tests supplied already-correct B props and did not cover that transition. A new ordinary parent/router test exposes the actual received props: its first run is18 passing/1 failing, with `43|Saved answer|Saved recruiter message` incorrectly present while B's GET is held. + +The aggregate now projects only data whose ID matches the current job, the answers editor waits for that aggregate, and read generations invalidate previous job/locale/unmounted completions. Same-job refresh still retains the current editor and its dirty state. The unchanged18 parent tests plus the new case pass; all affected assets28/parent19/helper3/namedcontrols5 yield **55/4** at21:15:52.411907–21:16:08.617731UTC. An intermediate52/3 run accidentally named a nonexistent helper file; it did execute those52 cases and is retained, but is not evidence for the helper3. This one addition derives **717/94 frontend**, replacing716/94 only as a derived count. + +A tenth native case exercises actual command-palette SPA navigation, a held B aggregate, absence of foreign draft fields, explicit one-field B save and real-HTTP readback of both jobs. Its final execution, export/source/static/push observations remain pending. Ten new ordinary cases would derive **283 browser**, without claiming an integrated run. Previous55-scan and latest54-scan observations above identify their actual earlier source/export; they do not prove this later parent change. + +Generated Tailoring titles/details remain server English in Bokmål; raw version source/action enums remain P7-11 localisation residuals. User-authored text is not automatically translated. Existing frame-level translation-dependent reload, attachment ID0 and AI-text freshness remain separately scoped. RD21, M01–M12 human reader/device/software keyboard/zoom, fonts/photo/native print/other-browser/provider/release requirements stay open. All57 groups remain56 PARTIAL/PRES-C11 DEFERRED and32 routes; P5-13 remains withheld,06F deferred, production NO-GO/main27657f3 unchanged and Phase8 separate. Inbox/read review and confirmed compose safety work remain the next two sequential boundaries. + +The first parent-change native observation is retained in `forms/attempt-6-parent-9sM6acEkOGxgN5fTRZXdE/results.json` (SHA25627517dce65e6b68e1d9c1268776df9ddc458b063701a076437ce20da2bb9d1f0):7 pass/3 fail at21:17:34.149961–21:19:12.484298UTC. The new fixture incorrectly expected201 when the company already existed (the endpoint correctly returned200); it now reuses the owned company ID and retains the job201 assertion. The frozen dialog check set scrollTop0 while keyboard PageDown animation was still running, observed8 and failed; it now verifies keyboard Home returns0 after actual PageDown. Neither changes frozen byte/persistence assertions. The third failure remains strict real locale204→500. Corrected test-only execution is pending; no failed run is discarded. + +Final parent-source forms run at21:19:34.115654–21:21:37.836490UTC is **9 pass/1 fail of10**, retaining the ordinary first-Bokmål204→500 test. The new same-document navigation and exact two-job readback case passes; PageDown→Home and frozen HTML/file bytes pass. All23 current forms PNGs were actually displayed/reviewed, including the held B empty editor and saved B ownership views and the failed actual-language screenshot. Earlier22-image reviews remain separate. Affected and integrated counts remain distinct: latest frontend55/4; final count derivation717/94 and283 browser only. + +The authoritative final capsule is `forms/parent-final/results.json`, SHA256e1ff6f5c9928a765630db3229b1ae88312e65cea82d24efabc16ccec6a817c1e, with23/111 final forms images actually reviewed. Final triage `forms/axe-triage-parent.json` SHA25651f6adcb2b205f0a983c73646864a53ba2b724a453217c3f5390d0520e35da67 records currentforms22/154 plus currentJobs8/89 and earlierqualifiedCV12/121,Career13/377:55scans0violations/741incomplete. Twenty matchedforms scans retain7semantic removals;2newB captures and2missingstrictlocale captures explain scope changes. This is not improved contrast. All current snapshots/74export files match;6of7pre-native files match, native fixture/Home changes explicitly qualified. Exportdigest e91f356cb09a8e828ad10e6497eb2927d5e7159b48907ddcd7132458486f9a1f. FreshJobs5 at21:21:45.248272–21:23:27.806820 andordinaryP2-D01 at21:23:27.812337–21:23:39.684013 PASS; earlier CV4/Career11 remain correct as-run qualifications. SourceWorkspace7a0fb891f2632b35a8267ca0143a68522334f20bd2b2eda57b3ed2661e357934, nativeb349b5190226b0d4b5e4cfd9a56d0bd5507c7ad5da328f11d5b38829cb271914. Final static/normal push observation is separate. + +Final hygiene at21:26:32–35UTC passes tracked/untracked secret scan, working/staged/Phase7 whitespace,14document465relative-link checks,57groups and32original routes. Local development HEAD/origin still627955a after fresh fetch; main27657f3 unchanged. Normal development commit/push receipt follows in the inbox/read successor record, avoiding a self-referential commit hash. RD11 remains ordinary failing and mandatory; independent safe development can continue. diff --git a/docs/redesign/phase-7/p7-06/README.md b/docs/redesign/phase-7/p7-06/README.md new file mode 100644 index 0000000..8047ef7 --- /dev/null +++ b/docs/redesign/phase-7/p7-06/README.md @@ -0,0 +1,46 @@ +# P7-06 — Remaining Application forms and global correspondence + +**Embedded forms: CONDITIONAL GO; final static checks and normal push pending. Overall P7-06 remains ongoing.** The [bounded forms report](FORMS.md) records repairs, meaningful failures and finite evidence, including the unchanged strict language-save HTTP500 failure (RD11). Follow the exact [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries). P7-06 has exactly three sequential implementation commits: embedded forms, inbox/read review, then compose/send. Optional P7-06F remains **DEFERRED** until its exact mode→draft/review action and fallback are decided. + +[Entry evidence](evidence/entry.json) records the actual Reminders normal push `627955afa989d32fefa53f15da9ef3e0abf27038`, directly observed2026-10-13 11:49:32.473200UTC: localHEAD=origin=live development, clean worktree at observation, local/origin/live main unchanged `27657f31131cd95d42f0e1290853ec9c9b3959a4`. P7-05 implemented scope is CONDITIONAL GO. Its push adds no new runtime, human or production acceptance. + +## Fresh pre-change baseline + +The existing affected Jest baseline passes **39 cases/3 suites**: Application assets18, workspace overlay18 and workspace helper3, with zero failed, skipped/pending or todo cases. It ran2026-10-13 11:39:16.918910–11:39:27.061883UTC at HEAD `c723a4e3032185da623d81ace241eec748506fc7` with uncommitted Reminders work. All seven captured relevant sources remained unchanged through the run. This earlier source/run capsule is separate from the later clean627955a receipt. + +Exact ignored capsule: `.artifacts/phase7/p7-06/forms/unit-baseline`. Its raw JSON report is `p706-forms-baseline-20261013-review-20261013T113916Z.json`, SHA256 `6cd3a67d35f0172d3c7de32287a72c2f1fd3aee6b6bcee15474f7ceaeff3f81e`. Portable entry retains the command, source hashes, raw report descriptors and after-run source equality. Existing mocked tests establish a baseline; they do not establish a repair or native acceptance. + +## Sequential boundaries and required proof + +| Order | Existing owner and bounded work | Required exit | +|---|---|---| +| 1 — Embedded forms | `ApplicationAssets.tsx`; retain the seven-section workspace, table, routes and renderer. Correct loaded-state/error/retry/dirty/AI review presentation and synchronous operation ownership in this owner | Meaningful RED and focused unit/native proof for read error→GET-only Retry, loaded dirty save/restore failure, long letter/answers/history, dirty Cancel/Restore, locale change without resource reads/writes and held Save/AI Apply/action context. Preserve current data/readback and frozen package bytes | +| 2 — Inbox/read review | Existing CorrespondenceInboxPage/GmailReviewPage and review alias; retain list/detail/filter/page/back/provider ownership | Native populated/empty/loading/error/retry/foreign cases, filters/pages and detail→Back context, keyboard/focus/reflow and explicit review choices; no compose persistence restructuring | +| 3 — Compose/send | Existing draft revision, confirmation and delivery owners; repair the [confirmed compose safety dependency](../DEBT.md#p7-06-confirmed-compose-safety-entry-dependency) coherently at this boundary | Meaningful runtime RED, captured-identity/synchronous Save–Send protection, complete dirty fields, revision/stale-conflict recovery, explicit confirmed send, uncertain-delivery protection and deliberate definitive-failure new attempt; no automatic send/resend | + +The first slice entered with confirmed code-path findings: mutation errors hide loaded dirty controls/history; initial GET failures lack Retry; locale changes trigger reads; dirty Restore has no confirmation; AI Apply during parent Save can lose edits and a mutable action can misattribute its suggestion. [FORMS.md](FORMS.md) records repairs, exact operation/identity guards and attachment recovery. Latest54/4 units pass. Current buildxyh9FYyySEw1uIao4DveJ native8/1 retains only the strict real-account language-save204→HTTP500 EN failure; keyboard PageDown passes. Fresh current Jobs5/P2-D01ordinary1 give14pass/1fail of15; previous-build CV4/Career11 extend the relevant count to29pass/1fail of30, retaining their as-run source/build qualifiers. Final static/push proof is pending; RD11 is not waived or closed. + +## Preserved contracts + +- One CV variant per application; current source, association and tenant/resource boundaries. +- Cover-letter source/action and additive owned history; dirty discard remains deliberate. +- Bodyless restore without ExpectedVersion; [RD21](../DEBT.md) remains open rather than becoming a hidden concurrency rewrite. +- The two existing application answers use their current Notes encoding and clearing behavior. +- Draft/frozen packages, archived attachments and native P2-D01 byte proof remain unchanged. +- Existing shared MUI/theme/state/confirm/error/i18n owners; no generic Documents persistence or second renderer. + +Follow EN/Bokmål, light/dark, narrow/long forms, density, keyboard/focus/error and relevant viewport/zoom requirements from the handoff. Capture exact source/run artifacts, inspect final images and triage all-enabled axe vectors without replacing applicable human/device checks. + +## Current disposition and retained gates + +All57 preservation groups remain **56 PARTIAL /PRES-C11 DEFERRED** and32 routes. Entry frontend **706/94**, browser **273** and backend **1,034** are **derived only**. Latest full frontend573/89, backend1,033 and sidecar49 remain inherited; P7-12 owns fresh integrated regression. No P7-06 additions are claimed by this baseline. + +The forms slice adds10 frontend cases/zero suites and9 ordinary browser cases, deriving **716/94 frontend** and **282 browser cases** only. Latest affected54/4 includes existing named controls5/1, which are not counted again. Existing backend30/30 no-build and qualified RichText/workspace consumer evidence do not establish a new whole-suite run or complete forms acceptance. All22 current forms PNGs from `final-verified` were actually displayed/reviewed:22/110 total, with88 other retained images; corrected attachment-failure copy is confirmed. The earlier22/110 review remains separate. Sticky composites/scaled long images retain pixel/occlusion/zoom/device limits. Prior55 scans0/749 stay qualified; no new axe total is inferred. Final portable/static/hygiene/push records remain pending, with production NO-GO. + +The current reviewed capsule `.artifacts/phase7/p7-06/forms/final-verified/results.json` has SHA256 `28da994d574e819208d65edc756fac13eca0597a9d622bcd47a3eb488db7eea4`; captured `ApplicationAssets.tsx` has SHA256 `d3be34c85fd4ab8984356f5fdc8a5b741590120e4253f97246bf870486d027c1`. [FORMS.md](FORMS.md) retains the finite current/prior-source and visual qualifications. + +M01–M12 remain NOT VERIFIED. Actual reader/device/software keyboard/browser zoom, deployed fonts/photo/native print/other browser, provider and release qualifications remain. P5-13 stays withheld; P7-06F stays deferred; production remains **NO-GO**, main unchanged and Phase8 has no implementation. Embedded forms continue first; inbox/read review and compose/send remain separate later commits. + +### Final P7-06 embedded-forms observation + +Final embedded-forms implemented scope is **CONDITIONAL GO**, normal push pending. The matching aggregate/generation repair resolves the reviewed cross-job draft leak: ordinary parent RED18/1, latest affected **55/4 PASS** and actual SPA/heldB/exact two-job readback PASS. Final parent build9sM6acEkOGxgN5fTRZXdE has **9PASS/1FAIL of10** (unchanged strict actual NB language204→500/RD11); fresh Jobs5/P2-D01ordinary1 give **15PASS/1FAIL of16**, qualified earlier CV4/Career11 give **30PASS/1FAIL relevant31**. Backend30 sameDLL/no-build; app/browser types/lint/build PASS. All23/111 current forms PNGs actually reviewed, 55 recorded axe scans0/741 (current22/154+Jobs8/89; earlierCV12/121+Career13/377), full incomplete contexts pending human qualification. See [portable forms result](evidence/forms-results.json). +11frontend/0suites derives **717/94**; +10browser derives **283**, no integrated run. All57 groups56PARTIAL/C11DEFERRED and32routes, P513withheld/06Fdeferred/main unchanged/productionNO-GO. Inbox/read and compose/send remain; Phase8 not started. diff --git a/docs/redesign/phase-7/p7-06/evidence/entry.json b/docs/redesign/phase-7/p7-06/evidence/entry.json new file mode 100644 index 0000000..4eed0c2 --- /dev/null +++ b/docs/redesign/phase-7/p7-06/evidence/entry.json @@ -0,0 +1,350 @@ +{ + "schemaVersion": 1, + "checkpoint": "P7-06", + "slice": "Embedded Application forms entry", + "acceptance": "ENTRY_RECORDED_EMBEDDED_FORMS_ONGOING_RUNTIME_ACCEPTANCE_PENDING", + "previousRemindersPush": { + "observedUtc": "2026-10-13T11:49:32.473200+00:00", + "branch": "codex/ui-redesign-phase4", + "head": "627955afa989d32fefa53f15da9ef3e0abf27038", + "originDevelopment": "627955afa989d32fefa53f15da9ef3e0abf27038", + "liveDevelopment": "627955afa989d32fefa53f15da9ef3e0abf27038", + "main": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "originMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "liveMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "workingTreeClean": true, + "pendingPaths": [], + "commands": [ + { + "command": [ + "rtk", + "proxy", + "git", + "branch", + "--show-current" + ], + "exitCode": 0, + "stdout": "codex/ui-redesign-phase4\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "HEAD" + ], + "exitCode": 0, + "stdout": "627955afa989d32fefa53f15da9ef3e0abf27038\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/codex/ui-redesign-phase4" + ], + "exitCode": 0, + "stdout": "627955afa989d32fefa53f15da9ef3e0abf27038\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\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": "627955afa989d32fefa53f15da9ef3e0abf27038\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": "", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + } + ] + }, + "previousRemindersPushRaw": ".artifacts/phase7/p7-05/reminders/push-receipt.json", + "commitBoundaries": [ + { + "order": 1, + "scope": "Embedded Application forms", + "status": "ONGOING", + "productOwner": "job-tracker-ui/src/components/ApplicationAssets.tsx" + }, + { + "order": 2, + "scope": "Inbox/read review", + "status": "PENDING", + "productOwner": "Existing CorrespondenceInboxPage/GmailReviewPage owners" + }, + { + "order": 3, + "scope": "Compose/send", + "status": "PENDING", + "prerequisite": "Confirm meaningful runtime RED and repair captured identity/save/delivery safeguards before acceptance" + } + ], + "optionalFollowMode": { + "checkpoint": "P7-06F", + "status": "DEFERRED", + "requiredDecision": "Exact mode-to-draft/review action and fallback; never auto-send or auto-resend" + }, + "baseline": { + "capturedUtc": "2026-10-13T11:39:11.302034+00:00", + "head": "c723a4e3032185da623d81ace241eec748506fc7", + "sourceCommitQualification": "This pre-change run occurred at c723a4e with uncommitted Reminders work. It is separate from the later clean 627955a push receipt; no clean-627955a baseline is claimed.", + "command": { + "command": [ + "rtk", + "proxy", + "npm.cmd", + "test", + "--", + "--runInBand", + "--watchAll=false", + "--json", + "--outputFile=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\unit\\p706-forms-baseline-20261013-review-20261013T113916Z.json", + "src/application-assets.test.tsx", + "src/application-workspace-overlay.test.tsx", + "src/applicationWorkspace.test.ts" + ], + "startedUtc": "2026-10-13T11:39:16.918910+00:00", + "finishedUtc": "2026-10-13T11:39:27.061883+00:00", + "exitCode": 0, + "counts": { + "numPassedTests": 39, + "numFailedTests": 0, + "numPendingTests": 0, + "numTodoTests": 0, + "numPassedTestSuites": 3, + "numFailedTestSuites": 0 + } + }, + "suiteCounts": [ + { + "name": "application-workspace-overlay.test.tsx", + "passed": 18, + "failed": 0, + "pending": 0 + }, + { + "name": "application-assets.test.tsx", + "passed": 18, + "failed": 0, + "pending": 0 + }, + { + "name": "applicationWorkspace.test.ts", + "passed": 3, + "failed": 0, + "pending": 0 + } + ], + "reports": [ + { + "source": ".artifacts/phase7/current/unit/p706-forms-baseline-20261013-review-20261013T113916Z.json", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/p706-forms-baseline-20261013-review-20261013T113916Z.json", + "bytes": 15540, + "sha256": "6cd3a67d35f0172d3c7de32287a72c2f1fd3aee6b6bcee15474f7ceaeff3f81e" + }, + { + "source": ".artifacts/phase7/current/unit/p706-forms-baseline-20261013-review-20261013T113916Z.log", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/p706-forms-baseline-20261013-review-20261013T113916Z.log", + "bytes": 762, + "sha256": "627ec5ddc5b43fb6f1d36a2a6e06f8e96786b8f4efe96fab775884f60eff9a1c" + }, + { + "source": ".artifacts/phase7/current/unit/p706-forms-baseline-20261013-review-20261013T113916Z-command.json", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/p706-forms-baseline-20261013-review-20261013T113916Z-command.json", + "bytes": 753, + "sha256": "6745179da2994411470fbd8b014f5556272049699d43ecfde02c009eab497e96" + } + ], + "sourceSnapshotRaw": ".artifacts/phase7/p7-06/forms/unit-baseline/source-snapshot.json", + "reportSnapshotRaw": ".artifacts/phase7/p7-06/forms/unit-baseline/report-snapshot.json", + "afterRunSourceCheckRaw": ".artifacts/phase7/p7-06/forms/unit-baseline/after-run-source-check.json", + "baselineSources": [ + { + "source": "job-tracker-ui/src/components/ApplicationAssets.tsx", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/ApplicationAssets.tsx", + "bytes": 33172, + "sha256": "69a58c2153951dfad94a42ef0dd0c662dc943f845313596dd4063e8b38f56aa6" + }, + { + "source": "job-tracker-ui/src/application-assets.test.tsx", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/application-assets.test.tsx", + "bytes": 16119, + "sha256": "971d9f5088379592b3a8d66909805a7541c1b5d531481ae79e85fb850e2af8c1" + }, + { + "source": "job-tracker-ui/src/application-workspace-overlay.test.tsx", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/application-workspace-overlay.test.tsx", + "bytes": 18663, + "sha256": "7315773906ae702f5fd93b2c941f7a46c7aeeaabdf798a963f7d38dba09b6313" + }, + { + "source": "job-tracker-ui/src/applicationWorkspace.test.ts", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/applicationWorkspace.test.ts", + "bytes": 1894, + "sha256": "bd068a208d40f782f66f77e6196378f4fa2a5be6f0eacd71e4f6271276e89362" + }, + { + "source": "job-tracker-ui/src/i18n/translations.ts", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/translations.ts", + "bytes": 321263, + "sha256": "d12724a6d4a61a15879e485316eb0227135f7218baa26ada4beaf4e12c4b9b9b" + }, + { + "source": "job-tracker-ui/src/applicationWorkspace.ts", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/applicationWorkspace.ts", + "bytes": 14534, + "sha256": "73b5b8c894e1ff053a41cc8c8beb6baf66ca03146abaec5866377602345f6981" + }, + { + "source": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/ApplicationWorkspacePage.tsx", + "bytes": 31096, + "sha256": "26fc8d62b93ee3230842235d434dba97ce62b9ae1d6f38f3d735fbb4fbb749a4" + } + ], + "afterRunSourceCheck": { + "checkedUtc": "2026-10-13T11:39:51.530341+00:00", + "allUnchanged": true, + "sources": [ + { + "source": "job-tracker-ui/src/components/ApplicationAssets.tsx", + "beforeSha256": "69a58c2153951dfad94a42ef0dd0c662dc943f845313596dd4063e8b38f56aa6", + "afterSha256": "69a58c2153951dfad94a42ef0dd0c662dc943f845313596dd4063e8b38f56aa6", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/application-assets.test.tsx", + "beforeSha256": "971d9f5088379592b3a8d66909805a7541c1b5d531481ae79e85fb850e2af8c1", + "afterSha256": "971d9f5088379592b3a8d66909805a7541c1b5d531481ae79e85fb850e2af8c1", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/application-workspace-overlay.test.tsx", + "beforeSha256": "7315773906ae702f5fd93b2c941f7a46c7aeeaabdf798a963f7d38dba09b6313", + "afterSha256": "7315773906ae702f5fd93b2c941f7a46c7aeeaabdf798a963f7d38dba09b6313", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/applicationWorkspace.test.ts", + "beforeSha256": "bd068a208d40f782f66f77e6196378f4fa2a5be6f0eacd71e4f6271276e89362", + "afterSha256": "bd068a208d40f782f66f77e6196378f4fa2a5be6f0eacd71e4f6271276e89362", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/i18n/translations.ts", + "beforeSha256": "d12724a6d4a61a15879e485316eb0227135f7218baa26ada4beaf4e12c4b9b9b", + "afterSha256": "d12724a6d4a61a15879e485316eb0227135f7218baa26ada4beaf4e12c4b9b9b", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/applicationWorkspace.ts", + "beforeSha256": "73b5b8c894e1ff053a41cc8c8beb6baf66ca03146abaec5866377602345f6981", + "afterSha256": "73b5b8c894e1ff053a41cc8c8beb6baf66ca03146abaec5866377602345f6981", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx", + "beforeSha256": "26fc8d62b93ee3230842235d434dba97ce62b9ae1d6f38f3d735fbb4fbb749a4", + "afterSha256": "26fc8d62b93ee3230842235d434dba97ce62b9ae1d6f38f3d735fbb4fbb749a4", + "unchanged": true + } + ] + }, + "qualification": "39 passed/3 suites: assets18+overlay18+helper3, zero failed/pending/todo. All seven captured relevant sources remained unchanged through execution. Existing mocked Jest evidence is not a repair, native runtime acceptance or fresh whole-suite run." + }, + "firstSliceAcceptanceRequirements": [ + "Keep loaded dirty controls and owned history visible after failed save/restore; deliberate retry retains drafts.", + "Expose initial read error and explicit GET-only Retry.", + "Retranslate current locale without issuing resource reads or writes.", + "Confirm discarding dirty edits before Restore; cancellation performs no write.", + "Capture AI source/action context; synchronous Save/Apply/mutation guards prevent pending-save edit loss or suggestion misattribution." + ], + "preservedContracts": [ + "Existing seven-section Application workspace, routes, table and CV/document renderer.", + "One CV variant per job; current CV association and ownership semantics.", + "Cover-letter source/action and additive owned history.", + "Bodyless restore with no ExpectedVersion: retain RD21 limitations.", + "Existing two-answer Notes encoding and clearing behavior.", + "Native P2-D01/frozen package and archived binary byte contracts." + ], + "confirmedComposeDependency": "Existing updateDraft clears delivery state/rotates identity; held draft save, incomplete dirty checks and Save/Send identity races require a coherent repair at the third approved boundary. Code-path findings are not passing runtime proof; no mail/provider/backend/schema change is claimed by entry.", + "countProvenance": { + "wholeFrontendDerivedOnly": "706/94", + "browserDerivedOnly": 273, + "backendDerivedOnly": 1034, + "latestWholeFrontend": "573/89 inherited", + "latestWholeBackend": "1033 inherited", + "sidecar": "49 inherited", + "integratedAcceptance": "P7-12 pending", + "p706Additions": "None claimed by this entry baseline" + }, + "preservation": { + "groups": 57, + "statuses": { + "PARTIAL": 56, + "DEFERRED": 1, + "PASS": 0, + "FAIL": 0 + }, + "deferredGroup": "PRES-C11", + "currentRoutes": 32 + }, + "qualifications": [ + "M01-M12 human/device acceptance remains NOT VERIFIED; M13/P5-13 is deferred and production-withheld.", + "Actual reader/device/browser zoom, deployed fonts/photo/native-print/other-browser, provider and release gates remain.", + "No first-slice native acceptance, full frontend/backend/browser rerun, model/provider/send activity, production deployment or Phase8 work is claimed." + ], + "production": { + "main": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "originMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "liveMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "status": "NO-GO", + "phase8": "No implementation" + }, + "hashPolicy": "Baseline hashes describe exact captured raw bytes; portable entry is UTF8 LF." +} diff --git a/docs/redesign/phase-7/p7-06/evidence/forms-results.json b/docs/redesign/phase-7/p7-06/evidence/forms-results.json new file mode 100644 index 0000000..dac332b --- /dev/null +++ b/docs/redesign/phase-7/p7-06/evidence/forms-results.json @@ -0,0 +1,3431 @@ +{ + "schemaVersion": 1, + "checkpoint": "P7-06", + "slice": "Embedded application forms", + "recordedUtc": "2026-10-13T21:26:30.542966+00:00", + "acceptance": "FORMS_IMPLEMENTED_SCOPE_CONDITIONAL_GO_HYGIENE_PASS_NORMAL_PUSH_PENDING", + "overallCheckpoint": "ONGOING_INBOX_READ_AND_COMPOSE_SEND_REMAIN", + "entry": { + "schemaVersion": 1, + "checkpoint": "P7-06", + "slice": "Embedded Application forms entry", + "acceptance": "ENTRY_RECORDED_EMBEDDED_FORMS_ONGOING_RUNTIME_ACCEPTANCE_PENDING", + "previousRemindersPush": { + "observedUtc": "2026-10-13T11:49:32.473200+00:00", + "branch": "codex/ui-redesign-phase4", + "head": "627955afa989d32fefa53f15da9ef3e0abf27038", + "originDevelopment": "627955afa989d32fefa53f15da9ef3e0abf27038", + "liveDevelopment": "627955afa989d32fefa53f15da9ef3e0abf27038", + "main": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "originMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "liveMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "workingTreeClean": true, + "pendingPaths": [], + "commands": [ + { + "command": [ + "rtk", + "proxy", + "git", + "branch", + "--show-current" + ], + "exitCode": 0, + "stdout": "codex/ui-redesign-phase4\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "HEAD" + ], + "exitCode": 0, + "stdout": "627955afa989d32fefa53f15da9ef3e0abf27038\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/codex/ui-redesign-phase4" + ], + "exitCode": 0, + "stdout": "627955afa989d32fefa53f15da9ef3e0abf27038\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\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": "627955afa989d32fefa53f15da9ef3e0abf27038\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": "", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + } + ] + }, + "previousRemindersPushRaw": ".artifacts/phase7/p7-05/reminders/push-receipt.json", + "commitBoundaries": [ + { + "order": 1, + "scope": "Embedded Application forms", + "status": "ONGOING", + "productOwner": "job-tracker-ui/src/components/ApplicationAssets.tsx" + }, + { + "order": 2, + "scope": "Inbox/read review", + "status": "PENDING", + "productOwner": "Existing CorrespondenceInboxPage/GmailReviewPage owners" + }, + { + "order": 3, + "scope": "Compose/send", + "status": "PENDING", + "prerequisite": "Confirm meaningful runtime RED and repair captured identity/save/delivery safeguards before acceptance" + } + ], + "optionalFollowMode": { + "checkpoint": "P7-06F", + "status": "DEFERRED", + "requiredDecision": "Exact mode-to-draft/review action and fallback; never auto-send or auto-resend" + }, + "baseline": { + "capturedUtc": "2026-10-13T11:39:11.302034+00:00", + "head": "c723a4e3032185da623d81ace241eec748506fc7", + "sourceCommitQualification": "This pre-change run occurred at c723a4e with uncommitted Reminders work. It is separate from the later clean 627955a push receipt; no clean-627955a baseline is claimed.", + "command": { + "command": [ + "rtk", + "proxy", + "npm.cmd", + "test", + "--", + "--runInBand", + "--watchAll=false", + "--json", + "--outputFile=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\unit\\p706-forms-baseline-20261013-review-20261013T113916Z.json", + "src/application-assets.test.tsx", + "src/application-workspace-overlay.test.tsx", + "src/applicationWorkspace.test.ts" + ], + "startedUtc": "2026-10-13T11:39:16.918910+00:00", + "finishedUtc": "2026-10-13T11:39:27.061883+00:00", + "exitCode": 0, + "counts": { + "numPassedTests": 39, + "numFailedTests": 0, + "numPendingTests": 0, + "numTodoTests": 0, + "numPassedTestSuites": 3, + "numFailedTestSuites": 0 + } + }, + "suiteCounts": [ + { + "name": "application-workspace-overlay.test.tsx", + "passed": 18, + "failed": 0, + "pending": 0 + }, + { + "name": "application-assets.test.tsx", + "passed": 18, + "failed": 0, + "pending": 0 + }, + { + "name": "applicationWorkspace.test.ts", + "passed": 3, + "failed": 0, + "pending": 0 + } + ], + "reports": [ + { + "source": ".artifacts/phase7/current/unit/p706-forms-baseline-20261013-review-20261013T113916Z.json", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/p706-forms-baseline-20261013-review-20261013T113916Z.json", + "bytes": 15540, + "sha256": "6cd3a67d35f0172d3c7de32287a72c2f1fd3aee6b6bcee15474f7ceaeff3f81e" + }, + { + "source": ".artifacts/phase7/current/unit/p706-forms-baseline-20261013-review-20261013T113916Z.log", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/p706-forms-baseline-20261013-review-20261013T113916Z.log", + "bytes": 762, + "sha256": "627ec5ddc5b43fb6f1d36a2a6e06f8e96786b8f4efe96fab775884f60eff9a1c" + }, + { + "source": ".artifacts/phase7/current/unit/p706-forms-baseline-20261013-review-20261013T113916Z-command.json", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/p706-forms-baseline-20261013-review-20261013T113916Z-command.json", + "bytes": 753, + "sha256": "6745179da2994411470fbd8b014f5556272049699d43ecfde02c009eab497e96" + } + ], + "sourceSnapshotRaw": ".artifacts/phase7/p7-06/forms/unit-baseline/source-snapshot.json", + "reportSnapshotRaw": ".artifacts/phase7/p7-06/forms/unit-baseline/report-snapshot.json", + "afterRunSourceCheckRaw": ".artifacts/phase7/p7-06/forms/unit-baseline/after-run-source-check.json", + "baselineSources": [ + { + "source": "job-tracker-ui/src/components/ApplicationAssets.tsx", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/ApplicationAssets.tsx", + "bytes": 33172, + "sha256": "69a58c2153951dfad94a42ef0dd0c662dc943f845313596dd4063e8b38f56aa6" + }, + { + "source": "job-tracker-ui/src/application-assets.test.tsx", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/application-assets.test.tsx", + "bytes": 16119, + "sha256": "971d9f5088379592b3a8d66909805a7541c1b5d531481ae79e85fb850e2af8c1" + }, + { + "source": "job-tracker-ui/src/application-workspace-overlay.test.tsx", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/application-workspace-overlay.test.tsx", + "bytes": 18663, + "sha256": "7315773906ae702f5fd93b2c941f7a46c7aeeaabdf798a963f7d38dba09b6313" + }, + { + "source": "job-tracker-ui/src/applicationWorkspace.test.ts", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/applicationWorkspace.test.ts", + "bytes": 1894, + "sha256": "bd068a208d40f782f66f77e6196378f4fa2a5be6f0eacd71e4f6271276e89362" + }, + { + "source": "job-tracker-ui/src/i18n/translations.ts", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/translations.ts", + "bytes": 321263, + "sha256": "d12724a6d4a61a15879e485316eb0227135f7218baa26ada4beaf4e12c4b9b9b" + }, + { + "source": "job-tracker-ui/src/applicationWorkspace.ts", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/applicationWorkspace.ts", + "bytes": 14534, + "sha256": "73b5b8c894e1ff053a41cc8c8beb6baf66ca03146abaec5866377602345f6981" + }, + { + "source": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx", + "copy": ".artifacts/phase7/p7-06/forms/unit-baseline/ApplicationWorkspacePage.tsx", + "bytes": 31096, + "sha256": "26fc8d62b93ee3230842235d434dba97ce62b9ae1d6f38f3d735fbb4fbb749a4" + } + ], + "afterRunSourceCheck": { + "checkedUtc": "2026-10-13T11:39:51.530341+00:00", + "allUnchanged": true, + "sources": [ + { + "source": "job-tracker-ui/src/components/ApplicationAssets.tsx", + "beforeSha256": "69a58c2153951dfad94a42ef0dd0c662dc943f845313596dd4063e8b38f56aa6", + "afterSha256": "69a58c2153951dfad94a42ef0dd0c662dc943f845313596dd4063e8b38f56aa6", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/application-assets.test.tsx", + "beforeSha256": "971d9f5088379592b3a8d66909805a7541c1b5d531481ae79e85fb850e2af8c1", + "afterSha256": "971d9f5088379592b3a8d66909805a7541c1b5d531481ae79e85fb850e2af8c1", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/application-workspace-overlay.test.tsx", + "beforeSha256": "7315773906ae702f5fd93b2c941f7a46c7aeeaabdf798a963f7d38dba09b6313", + "afterSha256": "7315773906ae702f5fd93b2c941f7a46c7aeeaabdf798a963f7d38dba09b6313", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/applicationWorkspace.test.ts", + "beforeSha256": "bd068a208d40f782f66f77e6196378f4fa2a5be6f0eacd71e4f6271276e89362", + "afterSha256": "bd068a208d40f782f66f77e6196378f4fa2a5be6f0eacd71e4f6271276e89362", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/i18n/translations.ts", + "beforeSha256": "d12724a6d4a61a15879e485316eb0227135f7218baa26ada4beaf4e12c4b9b9b", + "afterSha256": "d12724a6d4a61a15879e485316eb0227135f7218baa26ada4beaf4e12c4b9b9b", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/applicationWorkspace.ts", + "beforeSha256": "73b5b8c894e1ff053a41cc8c8beb6baf66ca03146abaec5866377602345f6981", + "afterSha256": "73b5b8c894e1ff053a41cc8c8beb6baf66ca03146abaec5866377602345f6981", + "unchanged": true + }, + { + "source": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx", + "beforeSha256": "26fc8d62b93ee3230842235d434dba97ce62b9ae1d6f38f3d735fbb4fbb749a4", + "afterSha256": "26fc8d62b93ee3230842235d434dba97ce62b9ae1d6f38f3d735fbb4fbb749a4", + "unchanged": true + } + ] + }, + "qualification": "39 passed/3 suites: assets18+overlay18+helper3, zero failed/pending/todo. All seven captured relevant sources remained unchanged through execution. Existing mocked Jest evidence is not a repair, native runtime acceptance or fresh whole-suite run." + }, + "firstSliceAcceptanceRequirements": [ + "Keep loaded dirty controls and owned history visible after failed save/restore; deliberate retry retains drafts.", + "Expose initial read error and explicit GET-only Retry.", + "Retranslate current locale without issuing resource reads or writes.", + "Confirm discarding dirty edits before Restore; cancellation performs no write.", + "Capture AI source/action context; synchronous Save/Apply/mutation guards prevent pending-save edit loss or suggestion misattribution." + ], + "preservedContracts": [ + "Existing seven-section Application workspace, routes, table and CV/document renderer.", + "One CV variant per job; current CV association and ownership semantics.", + "Cover-letter source/action and additive owned history.", + "Bodyless restore with no ExpectedVersion: retain RD21 limitations.", + "Existing two-answer Notes encoding and clearing behavior.", + "Native P2-D01/frozen package and archived binary byte contracts." + ], + "confirmedComposeDependency": "Existing updateDraft clears delivery state/rotates identity; held draft save, incomplete dirty checks and Save/Send identity races require a coherent repair at the third approved boundary. Code-path findings are not passing runtime proof; no mail/provider/backend/schema change is claimed by entry.", + "countProvenance": { + "wholeFrontendDerivedOnly": "706/94", + "browserDerivedOnly": 273, + "backendDerivedOnly": 1034, + "latestWholeFrontend": "573/89 inherited", + "latestWholeBackend": "1033 inherited", + "sidecar": "49 inherited", + "integratedAcceptance": "P7-12 pending", + "p706Additions": "None claimed by this entry baseline" + }, + "preservation": { + "groups": 57, + "statuses": { + "PARTIAL": 56, + "DEFERRED": 1, + "PASS": 0, + "FAIL": 0 + }, + "deferredGroup": "PRES-C11", + "currentRoutes": 32 + }, + "qualifications": [ + "M01-M12 human/device acceptance remains NOT VERIFIED; M13/P5-13 is deferred and production-withheld.", + "Actual reader/device/browser zoom, deployed fonts/photo/native-print/other-browser, provider and release gates remain.", + "No first-slice native acceptance, full frontend/backend/browser rerun, model/provider/send activity, production deployment or Phase8 work is claimed." + ], + "production": { + "main": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "originMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "liveMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "status": "NO-GO", + "phase8": "No implementation" + }, + "hashPolicy": "Baseline hashes describe exact captured raw bytes; portable entry is UTF8 LF." + }, + "immutableFinalCapsule": { + "path": ".artifacts/phase7/p7-06/forms/parent-final/results.json", + "sha256": "e1ff6f5c9928a765630db3229b1ae88312e65cea82d24efabc16ccec6a817c1e", + "bytes": 436107, + "hashPolicy": "Exact raw bytes" + }, + "verifiedFileReferences": 459, + "sourceAndExportMatch": true, + "preNativeSourceReceipt": { + "path": ".artifacts/phase7/p7-06/forms/parent-identity-pre-native/receipt.json", + "sha256": "5bf8a7dd6981a1f7ff013208c22cda9a5fe1ea164ab907f1f868d306b77ee534", + "bytes": 1503, + "hashPolicy": "Exact raw bytes" + }, + "units": [ + { + "raw": { + "path": ".artifacts/phase7/p7-06/forms/parent-final/unit/p706-parent-identity-complete-green-20261013-20261013T211552Z.json", + "sha256": "7f91fab12486f4e480f992098d3b54ab65ef04eb8f0ae7fe65d75c110f41a3a6", + "bytes": 22633, + "hashPolicy": "Exact measured raw bytes" + }, + "startedUnixMs": 1791926153332, + "passed": 55, + "failed": 0, + "pending": 0, + "total": 55, + "suites": 4, + "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": "opens the dedicated workspace from the whole row and preserves list state on return", + "status": "passed" + }, + { + "name": "opens a direct workspace URL and returns to applications", + "status": "passed" + }, + { + "name": "row controls do not trigger navigation", + "status": "passed" + }, + { + "name": "handles a deleted or inaccessible job without rendering a broken workspace", + "status": "passed" + }, + { + "name": "warns before section navigation would discard application edits", + "status": "passed" + }, + { + "name": "renders shared workspace navigation and progress in Norwegian Bokmål", + "status": "passed" + }, + { + "name": "hydrates list filters, sort and page from a shareable URL", + "status": "passed" + }, + { + "name": "names the progress scroll region and identifies the current stage without making stages actions", + "status": "passed" + }, + { + "name": "keeps custom status literal without claiming a completed standard pipeline", + "status": "passed" + }, + { + "name": "retries a failed workspace read without creating or changing application content", + "status": "passed" + }, + { + "name": "keeps the server-selected next action and preparation entry on the existing CV section", + "status": "passed" + }, + { + "name": "uses recorded dates in the summary and keeps absent dates unknown", + "status": "passed" + }, + { + "name": "section changes retain follow-up query context and expose the selected labelled panel", + "status": "passed" + }, + { + "name": "accordion controls identify exactly one labelled content region", + "status": "passed" + }, + { + "name": "legacy document links reveal current attachments and frozen history in the named section", + "status": "passed" + }, + { + "name": "the missing-profile next task opens the existing canonical profile entry", + "status": "passed" + }, + { + "name": "application subsection navigation retains metadata and protects unsaved answers", + "status": "passed" + }, + { + "name": "legacy correspondence and notes reveal distinct domain content", + "status": "passed" + }, + { + "name": "holds answers until their aggregate matches the route and ignores a late previous-job aggregate", + "status": "passed" + }, + { + "name": "[PRES-C08 PRES-C09] en CV selects have visible names and retain exact override patches", + "status": "passed" + }, + { + "name": "[PRES-C08 PRES-C09] nb CV selects have visible names and retain exact override patches", + "status": "passed" + }, + { + "name": "[PRES-C07 PRES-W06] en rich text retains names, selected Markdown and editor focus", + "status": "passed" + }, + { + "name": "[PRES-C07 PRES-W06] nb rich text retains names, selected Markdown and editor focus", + "status": "passed" + }, + { + "name": "[PRES-C07 PRES-W06] disabled rich text cannot format or edit and repeated fields keep separate labels", + "status": "passed" + }, + { + "name": "workspace navigation exposes seven purposes and preserves legacy links", + "status": "passed" + }, + { + "name": "legacy material and correspondence links reveal their original owner without rewriting URLs", + "status": "passed" + }, + { + "name": "canonical subsection URLs are durable and unknown inputs have safe defaults", + "status": "passed" + } + ], + "commandRaw": { + "path": ".artifacts/phase7/p7-06/forms/parent-final/unit/p706-parent-identity-complete-green-20261013-20261013T211552Z-command.json", + "sha256": "9276e78ed71f2a0fbc51f67dfcdbeedd8914439fd07160db881f5e8057ca4483", + "bytes": 798, + "hashPolicy": "Exact measured raw bytes" + }, + "logRaw": { + "path": ".artifacts/phase7/p7-06/forms/parent-final/unit/p706-parent-identity-complete-green-20261013-20261013T211552Z.log", + "sha256": "b9aeb6591d8ac086a730cb87990837426ab72dc756b0a30a711b5507253590d9", + "bytes": 837, + "hashPolicy": "Exact measured raw bytes" + }, + "qualification": "Raw executed outcome; source bytes are not inferred retroactively. Passing subset is not slice acceptance." + } + ], + "native": { + "application-forms": { + "command": { + "name": "application-forms", + "command": [ + "rtk", + "proxy", + "node", + "node_modules/@playwright/test/cli.js", + "test", + "e2e/phase7-application-forms.spec.ts", + "--retries=0", + "--reporter=list,json", + "--output=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\ui\\application-forms-results" + ], + "startedUtc": "2026-10-13T21:19:34.115654+00:00", + "finishedUtc": "2026-10-13T21:21:37.836490+00:00", + "exitCode": 1, + "failureLog": "E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\ui\\application-forms.log" + }, + "commandRaw": { + "path": ".artifacts/phase7/p7-06/forms/parent-final/application-forms-command.json", + "sha256": "51bdd222516ef05e7a6e37e6c7da25e9da866a56f3d1f0832fb26b69f138f709", + "bytes": 613, + "hashPolicy": "Exact measured raw bytes" + }, + "reportRaw": { + "path": ".artifacts/phase7/p7-06/forms/parent-final/application-forms.json", + "sha256": "7c73a7b6cc05acaed360b2bb820034fa00735445750e29c2e56525da850aaa1e", + "bytes": 4633426, + "hashPolicy": "Exact measured raw bytes" + }, + "logRaw": { + "path": ".artifacts/phase7/p7-06/forms/parent-final/application-forms.log", + "sha256": "b51df26b6fae589967c1315b7e1f1c902906ca9cbcff38f5f2b0ecde78b4bd8e", + "bytes": 5589, + "hashPolicy": "Exact measured raw bytes" + }, + "stats": { + "startTime": "2026-10-13T21:19:34.633Z", + "duration": 123133.933, + "expected": 9, + "skipped": 0, + "unexpected": 1, + "flaky": 0 + }, + "cases": [ + { + "title": "[P7-06 PRES-W01 W04 W10] same-route job navigation waits for owned answers and preserves both jobs on save", + "file": "phase7-application-forms.spec.ts", + "line": 160, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 5798 + }, + { + "title": "[P7-06 forms] held CV read Retry preserves its request and actual attach/replace/detach remain owner scoped", + "file": "phase7-application-forms.spec.ts", + "line": 223, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 4000 + }, + { + "title": "[P7-06 forms] long letter failed held Save retains snapshot and dirty restore Cancel writes nothing before additive explicit restore", + "file": "phase7-application-forms.spec.ts", + "line": 261, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 4793 + }, + { + "title": "[P7-06 forms] failed held Answers save preserves both drafts and ordinary Notes through explicit recovery and reload", + "file": "phase7-application-forms.spec.ts", + "line": 301, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 5172 + }, + { + "title": "[P7-06 forms] actual native capture freezes CV and exact archive bytes after current sources change without renderer mutation", + "file": "phase7-application-forms.spec.ts", + "line": 329, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 31538 + }, + { + "title": "[P7-06 forms] failed preserved detail and download retain history and Retry only the original owned read", + "file": "phase7-application-forms.spec.ts", + "line": 367, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 3995 + }, + { + "title": "[P7-06 forms] live EN/Bokmal retains dirty letter and both answer drafts without asset GET or writes", + "file": "phase7-application-forms.spec.ts", + "line": 394, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "failed" + ], + "retries": [ + 0 + ], + "errors": [ + [ + { + "location": { + "file": "E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\phase7-application-forms.spec.ts", + "column": 40, + "line": 401 + }, + "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m204\u001b[39m\nReceived: \u001b[31m500\u001b[39m\n\n 399 | const firstLocaleResponse = await firstLocale;\n 400 | await info.attach('application-locale-first-http', { body: JSON.stringify({ status: firstLocaleResponse.status(), body: await firstLocaleResponse.text() }), contentType: 'application/json' });\n> 401 | expect(firstLocaleResponse.status()).toBe(204);\n | ^\n 402 | await expect(page.locator('html')).toHaveAttribute('lang', 'nb-NO'); await expect(letter(page, 'nb')).toHaveValue('Dirty owner text remains the same across UI locale — æøå');\n 403 | expect(assetReads(session, seeded.job.id)).toHaveLength(reads); expect(mutations(session)).toEqual([]);\n 404 | await capture(page, info, { ...desktop, language: 'nb' }, 'dirty-letter-live-locale');\n at E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\phase7-application-forms.spec.ts:401:40" + } + ] + ], + "durationMs": 1591 + }, + { + "title": "[P7-06 forms] real duplicate then failed attach preserves created copy and original without automatic duplicate retry", + "file": "phase7-application-forms.spec.ts", + "line": 419, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 3583 + }, + { + "title": "[P7-06 forms] eight finite bilingual theme widths keep long CV, letter, drafts and frozen preview usable with keyboard", + "file": "phase7-application-forms.spec.ts", + "line": 455, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 50316 + }, + { + "title": "[P7-06 forms] controlled language HTTP503 preserves local dirty drafts and Retry only repeats the language request", + "file": "phase7-application-forms.spec.ts", + "line": 512, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 4258 + } + ], + "reportErrors": [], + "configuration": { + "version": "1.62.1", + "workers": 1, + "projects": [ + { + "name": "chromium", + "retries": 0 + } + ] + }, + "audit": { + "violationNodes": 0, + "incompleteNodes": 154, + "checkContexts": [ + { + "kind": "incomplete", + "rule": "color-contrast", + "check": "color-contrast", + "message": "Element content is too short to determine if it is actual text content", + "occurrences": 22, + "representativeContexts": [ + { + "attachment": "p706-forms-en-light-1440-held-second-job-aggregate-no-foreign-drafts-audit", + "target": [ + ".MuiBadge-badge" + ], + "data": { + "contrastRatio": 0, + "fontSize": "9.0pt (12px)", + "fontWeight": "normal", + "messageKey": "shortTextContent", + "expectedContrastRatio": "4.5:1" + }, + "relatedNodes": [] + }, + { + "attachment": "p706-forms-en-light-1440-second-job-answer-saved-first-job-preserved-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": 71, + "representativeContexts": [ + { + "attachment": "p706-forms-en-light-1440-held-second-job-aggregate-no-foreign-drafts-audit", + "target": [ + ".MuiButton-outlinedSizeMedium" + ], + "data": { + "contrastRatio": 0, + "fontSize": "10.5pt (14px)", + "fontWeight": "normal", + "messageKey": "bgOverlap", + "expectedContrastRatio": "4.5:1" + }, + "relatedNodes": [] + }, + { + "attachment": "p706-forms-en-light-1440-held-second-job-aggregate-no-foreign-drafts-audit", + "target": [ + ".css-13q0uwh" + ], + "data": { + "contrastRatio": 0, + "fontSize": "8.3pt (11px)", + "fontWeight": "bold", + "messageKey": "bgOverlap", + "expectedContrastRatio": "4.5:1" + }, + "relatedNodes": [] + } + ] + }, + { + "kind": "incomplete", + "rule": "aria-prohibited-attr", + "check": "aria-prohibited-attr", + "message": "aria-label attribute is not well supported on a span with no valid role attribute.", + "occurrences": 42, + "representativeContexts": [ + { + "attachment": "p706-forms-en-light-1440-drafts-saved-personal-notes-preserved-audit", + "target": [ + "section[aria-labelledby=\"_r_1n_\"] > .css-31l7gp.MuiBox-root > .css-qfnqh.MuiStack-root > .css-1c9ky8i.MuiStack-root:nth-child(1) > .checklist-actions.css-1iqpi3n.MuiStack-root > span[aria-label=\"Move up\"]" + ], + "data": { + "role": null, + "nodeName": "span", + "messageKey": "noRoleSingular", + "prohibited": [ + "aria-label" + ] + }, + "relatedNodes": [] + }, + { + "attachment": "p706-forms-en-light-1440-drafts-saved-personal-notes-preserved-audit", + "target": [ + "section[aria-labelledby=\"_r_1n_\"] > .css-31l7gp.MuiBox-root > .css-qfnqh.MuiStack-root > .css-1c9ky8i.MuiStack-root:nth-child(1) > .checklist-actions.css-1iqpi3n.MuiStack-root > span[aria-label=\"Move down\"]" + ], + "data": { + "role": null, + "nodeName": "span", + "messageKey": "noRoleSingular", + "prohibited": [ + "aria-label" + ] + }, + "relatedNodes": [] + } + ] + }, + { + "kind": "incomplete", + "rule": "aria-hidden-focus", + "check": "focusable-modal-open", + "message": "Check that focusable elements are not tabbable in the current state", + "occurrences": 3, + "representativeContexts": [ + { + "attachment": "p706-forms-en-light-1440-frozen-original-white-print-preview-audit", + "target": [ + "#root" + ], + "data": null, + "relatedNodes": [ + { + "html": "Skip to main content", + "target": [ + "a" + ] + }, + { + "html": "", + "target": [ + ".css-11kar8v" + ] + }, + { + "html": "
", + "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\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .Mui-selected[aria-current=\"page\"][aria-label=\"Job Applications\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(2) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(3) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(4) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(5) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(2) > .css-j56oom.MuiListItemButton-root[aria-label=\"Job email\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(3) > .MuiList-padding.css-k1qkeu[aria-label=\"Career\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-j56oom.MuiListItemButton-root[aria-label=\"Career profile\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(3) > .MuiList-padding.css-k1qkeu[aria-label=\"Career\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(2) > .css-j56oom.MuiListItemButton-root[aria-label=\"CV Builder\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(4) > .MuiList-padding.css-k1qkeu[aria-label=\"Insights\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-j56oom.MuiListItemButton-root[aria-label=\"Analytics\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root > .MuiList-padding.css-k1qkeu[aria-label=\"Account\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-j56oom.MuiListItemButton-root[aria-label=\"Settings\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root > .MuiList-padding.css-k1qkeu[aria-label=\"Account\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-1rv7atp[aria-label=\"Settings\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root > .MuiList-padding.css-k1qkeu[aria-label=\"Account\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-1rv7atp[aria-label=\"Settings\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(2) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "", + "target": [ + "#_r_n_-application" + ] + }, + { + "html": "
", + "target": [ + "#_r_n_-panel" + ] + }, + { + "html": "", + "target": [ + "#_r_o_-submission" + ] + }, + { + "html": "
", + "target": [ + "#_r_o_-panel" + ] + }, + { + "html": "", + "target": [ + ".css-1u962su" + ] + }, + { + "html": "", + "target": [ + "#_r_o_-application" + ] + }, + { + "html": "
", + "target": [ + "#_r_o_-panel" + ] + }, + { + "html": "
, + ApplicationPackageDraftsSection: ({ jobId, initialApplicationAnswer, initialRecruiterMessage, onDirtyChange }: { jobId: number; initialApplicationAnswer: string; initialRecruiterMessage: string; onDirtyChange?: (dirty: boolean) => void }) =>
Application drafts section{jobId}|{initialApplicationAnswer}|{initialRecruiterMessage}
, SubmittedPackagesSection: () =>
Frozen packages section
, })); jest.mock("./components/InterviewPrep", () => ({ ApplicationInterviewPrep: () =>
Interview section
})); @@ -136,7 +136,7 @@ function renderTable(path = "/jobs") { }, ], { initialEntries: [path] }); - return render( + const mounted = render( @@ -147,6 +147,7 @@ function renderTable(path = "/jobs") { , ); + return { ...mounted, router }; } beforeEach(() => { @@ -383,3 +384,30 @@ test("legacy correspondence and notes reveal distinct domain content", async () expect(screen.queryByText("Application drafts section")).not.toBeInTheDocument(); expect(screen.getByTestId("location")).toHaveTextContent("followMode=waiting-update"); }); + +test("holds answers until their aggregate matches the route and ignores a late previous-job aggregate", async () => { + let resolveB!: (value: any) => void; + let resolveC!: (value: any) => void; + const heldB = new Promise((resolve) => { resolveB = resolve; }); + const heldC = new Promise((resolve) => { resolveC = resolve; }); + const originalGet = mockedApi.get.getMockImplementation()!; + mockedApi.get.mockImplementation((url: string, ...rest: any[]) => { + if (url === "/jobapplications/43/workspace") return heldB; + if (url === "/jobapplications/44/workspace") return heldC; + return originalGet(url, ...rest); + }); + const { router } = renderTable("/jobs/42?section=application&sub=answers"); + expect(await screen.findByTestId("answer-props")).toHaveTextContent("42|Saved answer|Saved recruiter message"); + await act(async () => { await router.navigate("/jobs/43?section=application&sub=answers"); }); + await waitFor(() => expect(mockedApi.get).toHaveBeenCalledWith("/jobapplications/43/workspace")); + expect(screen.queryByTestId("answer-props")).not.toBeInTheDocument(); + await act(async () => { await router.navigate("/jobs/44?section=application&sub=answers"); }); + expect(screen.queryByTestId("answer-props")).not.toBeInTheDocument(); + await act(async () => { resolveC({ data: { ...overview, id: 44, jobTitle: "Job C", applicationAnswerDraft: "C answer", recruiterMessageDraft: "C message" } }); await heldC; }); + expect(await screen.findByTestId("answer-props")).toHaveTextContent("44|C answer|C message"); + await act(async () => { resolveB({ data: { ...overview, id: 43, jobTitle: "Late Job B", applicationAnswerDraft: "B answer", recruiterMessageDraft: "B message" } }); await heldB; }); + expect(screen.getByTestId("answer-props")).toHaveTextContent("44|C answer|C message"); + expect(screen.getByRole("heading", { name: "Job C" })).toBeInTheDocument(); + expect(mockedApi.post).not.toHaveBeenCalled(); + expect(mockedApi.put).not.toHaveBeenCalled(); +}); diff --git a/job-tracker-ui/src/components/ApplicationAssets.tsx b/job-tracker-ui/src/components/ApplicationAssets.tsx index e4e8c67..afa0103 100644 --- a/job-tracker-ui/src/components/ApplicationAssets.tsx +++ b/job-tracker-ui/src/components/ApplicationAssets.tsx @@ -19,6 +19,9 @@ import { cvBuilderApi } from "../cvBuilder"; import { aiWorkspaceApi } from "../aiWorkspace"; import { useAccountPlan } from "../accountPlan"; import { useI18n } from "../i18n/I18nProvider"; +import type { TranslationKey } from "../i18n/translations"; +import type { ViewResourceError } from "../hooks/useViewResource"; +import { useConfirm } from "../confirm"; import { PageSection } from "../layout/PageLayout"; import ViewStateNotice from "./ViewStateNotice"; import { extractApplicationAnswerDraft, removeApplicationAnswerDraft } from "../applicationDrafts"; @@ -30,52 +33,75 @@ import { extractApplicationAnswerDraft, removeApplicationAnswerDraft } from "../ // application uses and links out. The cover letter is the one thing genuinely owned here, because it // is application-specific by nature. docs/architecture/overview.md. +type AssetError = ViewResourceError & { messageKey: TranslationKey; retry?: () => Promise }; + +function assetError(error: unknown, messageKey: TranslationKey, retryable = false): AssetError { + const status = (error as { response?: { status?: number } })?.response?.status; + const unauthorized = status === 401 || status === 403; + return { kind: unauthorized ? "unauthorized" : "error", message: getApiErrorMessage(error, ""), messageKey, retryable: retryable && !unauthorized, status }; +} + function useAsset(load: () => Promise, deps: React.DependencyList) { - const { t } = useI18n(); const [data, setData] = useState(null); - const [error, setError] = useState(null); + const [error, setError] = useState(null); const [loading, setLoading] = useState(true); + const request = useRef(0); + const pending = useRef(false); + const active = useRef(true); // eslint-disable-next-line react-hooks/exhaustive-deps const run = useCallback(load, deps); - const reload = useCallback(() => { - let cancelled = false; + const reload = useCallback(async () => { + if (pending.current) return; + pending.current = true; + const current = ++request.current; setLoading(true); - run() - .then((r) => { - if (!cancelled) { - setData(r); - setError(null); - } - }) - .catch((err) => { - if (!cancelled) setError(getApiErrorMessage(err, t("assetsLoadFailed"))); - }) - .finally(() => { - if (!cancelled) setLoading(false); - }); - return () => { - cancelled = true; - }; - }, [run, t]); + try { + const next = await run(); + if (current !== request.current) return; + setData(next); + setError(null); + } catch (err) { + if (current === request.current) setError(assetError(err, "assetsLoadFailed", true)); + } finally { + if (current === request.current) { pending.current = false; setLoading(false); } + } + }, [run]); - useEffect(() => reload(), [reload]); + useEffect(() => { + active.current = true; + setData(null); + setError(null); + pending.current = false; + void reload(); + return () => { active.current = false; request.current += 1; pending.current = false; }; + }, [reload]); - return { data, error, loading, setData, setError, reload }; + return { data, error, loading, setData, setError, reload, isCurrent: () => active.current }; } -function Shell({ title, subtitle, loading, error, children }: { +function Shell({ title, subtitle, loading, error, available = true, onRetry, contentRef, children }: { title: string; subtitle?: string; loading: boolean; - error: string | null; + error: AssetError | null; + available?: boolean; + onRetry?: () => void | Promise; + contentRef?: React.RefObject; children: React.ReactNode; }) { + const { t } = useI18n(); + const localRef = useRef(null); + const bodyRef = contentRef ?? localRef; + const retry = error?.retry ?? onRetry; return ( - - {!loading && !error && children} + + { bodyRef.current?.focus(); return retry(); } : undefined} compact /> + {available && error?.kind !== "unauthorized" && children} + ); } @@ -84,20 +110,29 @@ function Shell({ title, subtitle, loading, error, children }: { export function ApplicationCvSection({ jobId, includePackages = true }: { jobId: number; includePackages?: boolean }) { const { t } = useI18n(); - const { data, error, loading, setData, setError } = useAsset( + const { data, error, loading, setData, setError, reload, isCurrent } = useAsset( () => applicationAssetsApi.cv(jobId), [jobId], ); const [busy, setBusy] = useState(false); + const busyRef = useRef(false); + const contentRef = useRef(null); + const [createdCopy, setCreatedCopy] = useState(null); const attach = async (variantId: number | null) => { + if (busyRef.current || loading || !data) return; + busyRef.current = true; + contentRef.current?.focus(); setBusy(true); try { - setData(await applicationAssetsApi.attachVariant(jobId, variantId)); + const next = await applicationAssetsApi.attachVariant(jobId, variantId); + if (!isCurrent()) return; + setData(next); setError(null); } catch (err) { - setError(getApiErrorMessage(err, t("assetsAttachCvFailed"))); + if (isCurrent()) setError(assetError(err, "assetsAttachCvFailed")); } finally { + busyRef.current = false; setBusy(false); } }; @@ -105,15 +140,24 @@ export function ApplicationCvSection({ jobId, includePackages = true }: { jobId: const attached = data?.attachedVariantId ?? ""; const duplicateForJob = async () => { - if (!data?.attachedVariantId) return; + if (busyRef.current || loading || !data?.attachedVariantId) return; + busyRef.current = true; + contentRef.current?.focus(); setBusy(true); + let copyId = createdCopy; try { - const copy = await cvBuilderApi.duplicate(data.attachedVariantId, t("assetsTailoredCopyName", { name: data.attachedVariantName || "CV" })); - setData(await applicationAssetsApi.attachVariant(jobId, copy.id)); - window.location.assign(`/career/builder/${copy.id}`); + copyId ??= (await cvBuilderApi.duplicate(data.attachedVariantId, t("assetsTailoredCopyName", { name: data.attachedVariantName || "CV" }))).id; + if (!isCurrent()) return; + setCreatedCopy(copyId); + const next = await applicationAssetsApi.attachVariant(jobId, copyId); + if (!isCurrent()) return; + setData(next); + setCreatedCopy(null); + window.location.assign(`/career/builder/${copyId}`); } catch (err) { - setError(getApiErrorMessage(err, t("assetsDuplicateCvFailed"))); + if (isCurrent()) setError(assetError(err, copyId ? "assetsAttachCvFailed" : "assetsDuplicateCvFailed")); } finally { + busyRef.current = false; setBusy(false); } }; @@ -123,8 +167,11 @@ export function ApplicationCvSection({ jobId, includePackages = true }: { jobId: {(data?.availableVariants.length ?? 0) === 0 ? ( @@ -138,7 +185,7 @@ export function ApplicationCvSection({ jobId, includePackages = true }: { jobId: fullWidth label={t("assetsAttachedCv")} value={attached} - disabled={busy} + disabled={busy || loading} onChange={(e) => attach(e.target.value === "" ? null : Number(e.target.value))} helperText={t("assetsAttachedCvHelp")} > @@ -172,8 +219,8 @@ export function ApplicationCvSection({ jobId, includePackages = true }: { jobId: > {t("assetsEditPreviewExport")} - @@ -189,6 +236,7 @@ export function ApplicationCvSection({ jobId, includePackages = true }: { jobId: {t("assetsLegacyCvText")} )} + {createdCopy && {t("assetsCopyCreatedHelp")}} @@ -202,49 +250,73 @@ export function SubmittedPackagesSection({ jobId, allowCapture = true }: { jobId const titleId = useId(); const viewTrigger = useRef(null); const { language, t } = useI18n(); - const { data, error, loading, setData, setError } = useAsset( + const { data, error, loading, setData, setError, reload, isCurrent } = useAsset( () => applicationAssetsApi.submittedPackages(jobId), [jobId], ); const [busy, setBusy] = useState(false); const [detail, setDetail] = useState(null); const [detailBusy, setDetailBusy] = useState(false); + const [downloadBusy, setDownloadBusy] = useState(false); + const operation = useRef(false); + const contentRef = useRef(null); + const pending = busy || detailBusy || downloadBusy || loading; const capture = async () => { + if (operation.current || loading || !data) return; + operation.current = true; + contentRef.current?.focus(); setBusy(true); try { const created = await applicationAssetsApi.captureSubmittedPackage(jobId); + if (!isCurrent()) return; setData([created, ...(data ?? [])]); setError(null); } catch (err) { - setError(getApiErrorMessage(err, t("assetsPackageCaptureFailed"))); + if (isCurrent()) setError(assetError(err, "assetsPackageCaptureFailed")); } finally { + operation.current = false; setBusy(false); } }; const download = async (item: SubmittedPackage, attachment: SubmittedPackage["attachments"][number]) => { + if (operation.current || loading || !data) return; + operation.current = true; + contentRef.current?.focus(); + setDownloadBusy(true); try { const blob = await applicationAssetsApi.downloadSubmittedAttachment(jobId, item.id, attachment.id); + if (!isCurrent()) return; const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); anchor.href = url; anchor.download = attachment.fileName; anchor.click(); URL.revokeObjectURL(url); + setError(null); } catch (err) { - setError(getApiErrorMessage(err, t("assetsPackageDownloadFailed"))); + if (isCurrent()) setError({ ...assetError(err, "assetsPackageDownloadFailed", true), retry: () => download(item, attachment) }); + } finally { + operation.current = false; + setDownloadBusy(false); } }; const view = async (item: SubmittedPackage) => { + if (operation.current || loading || !data) return; + operation.current = true; + contentRef.current?.focus(); setDetailBusy(true); try { - setDetail(await applicationAssetsApi.submittedPackage(jobId, item.id)); + const next = await applicationAssetsApi.submittedPackage(jobId, item.id); + if (!isCurrent()) return; + setDetail(next); setError(null); } catch (err) { - setError(getApiErrorMessage(err, t("assetsPackageLoadFailed"))); + if (isCurrent()) setError({ ...assetError(err, "assetsPackageLoadFailed", true), retry: () => view(item) }); } finally { + operation.current = false; setDetailBusy(false); } }; @@ -255,9 +327,9 @@ export function SubmittedPackagesSection({ jobId, allowCapture = true }: { jobId })(); return ( - + - {allowCapture && } {(data?.length ?? 0) === 0 ? ( @@ -277,11 +349,11 @@ export function SubmittedPackagesSection({ jobId, allowCapture = true }: { jobId - {item.attachments.map((attachment) => ( - ))} @@ -293,25 +365,25 @@ export function SubmittedPackagesSection({ jobId, allowCapture = true }: { jobId {detail ? t("assetsSubmittedPackageVersion", { version: detail.summary.version }) : t("assetsSubmittedPackages")} - + {t("workspaceFrozenHelp")} {detail?.renderedCvHtml ? ( - {t("workspaceCv")} + {t("workspaceCv")} {detail.summary.cvVariantName} · {t("assetsThemeVersion", { theme: detail.summary.cvThemeId ?? "—", version: detail.summary.cvVariantVersion ?? "—" })} ) : {t("assetsPackageNoCv")}} - {t("workspaceCoverLetter")} + {t("workspaceCoverLetter")} {detail?.coverLetterText || t("assetsPackageNoCoverLetter")} {material && ["TailoredCvText", "RecruiterMessageDraft"].map((key) => typeof material[key] === "string" && material[key] ? ( - {t(`assetsPackage${key}` as "assetsPackageTailoredCvText")} + {t(`assetsPackage${key}` as "assetsPackageTailoredCvText")} {String(material[key])} ) : null)} @@ -319,7 +391,7 @@ export function SubmittedPackagesSection({ jobId, allowCapture = true }: { jobId [t("workspaceNotes"), removeApplicationAnswerDraft(material.Notes)], [t("assetsApplicationAnswer"), extractApplicationAnswerDraft(material.Notes)], ].map(([label, content]) => content ? - {label} + {label} {content} : null)} @@ -334,7 +406,7 @@ export function SubmittedPackagesSection({ jobId, allowCapture = true }: { jobId export function ApplicationTailoringSection({ jobId }: { jobId: number }) { const { t } = useI18n(); - const { data, error, loading } = useAsset( + const { data, error, loading, reload } = useAsset( () => applicationAssetsApi.tailoring(jobId), [jobId], ); @@ -345,6 +417,8 @@ export function ApplicationTailoringSection({ jobId }: { jobId: number }) { subtitle={t("assetsTailoringSubtitle")} loading={loading} error={error} + available={data !== null} + onRetry={reload} > {data && !data.hasCareerProfile && ( @@ -413,13 +487,17 @@ Med vennlig hilsen export function ApplicationCoverLetterSection({ jobId, onDirtyChange }: { jobId: number; onDirtyChange?: (dirty: boolean) => void }) { const { language, t } = useI18n(); - const { data, error, loading, setData, setError } = useAsset( + const { confirm } = useConfirm(); + const { data, error, loading, setData, setError, reload, isCurrent } = useAsset( () => applicationAssetsApi.coverLetter(jobId), [jobId], ); const [draft, setDraft] = useState(null); const [draftAiAction, setDraftAiAction] = useState(null); const [busy, setBusy] = useState(false); + const [confirming, setConfirming] = useState(false); + const busyRef = useRef(false); + const contentRef = useRef(null); // The textarea is only seeded from the server until the user starts typing, so a reload never // clobbers unsaved edits. @@ -432,30 +510,53 @@ export function ApplicationCoverLetterSection({ jobId, onDirtyChange }: { jobId: }, [dirty, onDirtyChange]); const save = async (value: string, source = "manual", aiAction?: string) => { + if (busyRef.current || loading || !data) return; + busyRef.current = true; + contentRef.current?.focus(); setBusy(true); try { - setData(await applicationAssetsApi.saveCoverLetter(jobId, value, source, aiAction)); + const next = await applicationAssetsApi.saveCoverLetter(jobId, value, source, aiAction); + if (!isCurrent()) return; + setData(next); setDraft(null); setDraftAiAction(null); setError(null); } catch (err) { - setError(getApiErrorMessage(err, t("assetsCoverSaveFailed"))); + if (isCurrent()) setError(assetError(err, "assetsCoverSaveFailed")); } finally { + busyRef.current = false; setBusy(false); } }; const restore = async (version: number) => { - setBusy(true); + if (busyRef.current || loading || !data) return; + busyRef.current = true; try { - setData(await applicationAssetsApi.restoreCoverLetter(jobId, version)); + if (dirty) { + setConfirming(true); + const accepted = await confirm({ + title: t("assetsRestoreDiscardTitle"), message: t("assetsRestoreDiscardMessage"), + confirmLabel: t("assetsRestoreConfirm"), cancelLabel: t("cancel"), destructive: true, + }); + setConfirming(false); + if (!accepted) return; + } + if (!isCurrent()) return; + contentRef.current?.focus(); + setBusy(true); + const next = await applicationAssetsApi.restoreCoverLetter(jobId, version); + if (!isCurrent()) return; + setData(next); setDraft(null); setDraftAiAction(null); setError(null); } catch (err) { - setError(getApiErrorMessage(err, t("assetsCoverRestoreFailed"))); + if (isCurrent()) setError(assetError(err, "assetsCoverRestoreFailed")); } finally { + busyRef.current = false; setBusy(false); + setConfirming(false); } }; @@ -464,28 +565,31 @@ export function ApplicationCoverLetterSection({ jobId, onDirtyChange }: { jobId: - - }>{error}} + {error && void generate()}>{t("retry")}}>{error.message || t(error.messageKey)}} - + + @@ -701,7 +818,11 @@ export function ApplicationPackageDraftsSection({ const [saved, setSaved] = useState(initial); const [draft, setDraft] = useState(null); const [busy, setBusy] = useState(false); - const [error, setError] = useState(null); + const [error, setError] = useState(null); + const busyRef = useRef(false); + const contentRef = useRef(null); + const active = useRef(true); + useEffect(() => { active.current = true; return () => { active.current = false; }; }, []); useEffect(() => { if (draft === null) setSaved(initial); @@ -722,16 +843,21 @@ export function ApplicationPackageDraftsSection({ const update = (patch: Partial) => setDraft({ ...value, ...patch }); const save = async () => { + if (busyRef.current) return; + busyRef.current = true; + contentRef.current?.focus(); setBusy(true); try { await applicationAssetsApi.saveApplicationDrafts(jobId, value.applicationAnswer, value.recruiterMessage); + if (!active.current) return; setSaved(value); setDraft(null); setError(null); onSaved?.(); } catch (err) { - setError(getApiErrorMessage(err, t("assetsDraftsSaveFailed"))); + if (active.current) setError(assetError(err, "assetsDraftsSaveFailed")); } finally { + busyRef.current = false; setBusy(false); } }; @@ -740,8 +866,9 @@ export function ApplicationPackageDraftsSection({ ( - + e.preventDefault()} onClick={() => apply(before, after, ph)}> {icon} diff --git a/job-tracker-ui/src/i18n/translations.ts b/job-tracker-ui/src/i18n/translations.ts index 60cf352..04ad14f 100644 --- a/job-tracker-ui/src/i18n/translations.ts +++ b/job-tracker-ui/src/i18n/translations.ts @@ -508,6 +508,8 @@ export const translations = { assetsPublic: "public", assetsEditPreviewExport: "Edit, preview and export", assetsDuplicateForJob: "Duplicate for this job", + assetsAttachCreatedCopy: "Attach created copy", + assetsCopyCreatedHelp: "The copy is saved in your CV library. Attach it here to continue editing.", assetsNoCvAttached: "No CV is attached to this application yet.", assetsLegacyCvText: "This application also contains legacy tailored CV text. An attached CV variant takes precedence.", assetsTailoring: "Tailoring", @@ -958,6 +960,9 @@ export const translations = { assetsCharacterCount: "{count} characters", assetsRestoreVersion: "Restore this version", assetsRestoreVersionNumber: "Restore version {version}", + assetsRestoreDiscardTitle: "Restore saved version?", + assetsRestoreDiscardMessage: "Your unsaved edits will be discarded and replaced with the selected saved version.", + assetsRestoreConfirm: "Discard and restore", coverAiAction: "Action", coverAiTone: "Tone", coverActionGenerate: "Generate", @@ -3157,6 +3162,8 @@ export const translations = { assetsPublic: "offentlig", assetsEditPreviewExport: "Rediger, forhåndsvis og eksporter", assetsDuplicateForJob: "Dupliser for denne jobben", + assetsAttachCreatedCopy: "Koble til opprettet kopi", + assetsCopyCreatedHelp: "Kopien er lagret i CV-biblioteket. Koble den til her for å fortsette redigeringen.", assetsNoCvAttached: "Ingen CV er tilknyttet søknaden ennå.", assetsLegacyCvText: "Søknaden inneholder også eldre tilpasset CV-tekst. En tilknyttet CV-variant har prioritet.", assetsTailoring: "Tilpasning", @@ -3607,6 +3614,9 @@ export const translations = { assetsCharacterCount: "{count} tegn", assetsRestoreVersion: "Gjenopprett denne versjonen", assetsRestoreVersionNumber: "Gjenopprett versjon {version}", + assetsRestoreDiscardTitle: "Gjenopprette lagret versjon?", + assetsRestoreDiscardMessage: "Endringene du ikke har lagret, blir forkastet og erstattet med den valgte versjonen.", + assetsRestoreConfirm: "Forkast og gjenopprett", coverAiAction: "Handling", coverAiTone: "Tone", coverActionGenerate: "Generer", diff --git a/job-tracker-ui/src/views/ApplicationWorkspacePage.tsx b/job-tracker-ui/src/views/ApplicationWorkspacePage.tsx index fb03a35..3ed15fd 100644 --- a/job-tracker-ui/src/views/ApplicationWorkspacePage.tsx +++ b/job-tracker-ui/src/views/ApplicationWorkspacePage.tsx @@ -82,7 +82,9 @@ export function ApplicationWorkspace({ const secondaryId = useId(); const panelRef = useRef(null); - const [overview, setOverview] = useState(null); + const [loadedOverview, setOverview] = useState(null); + const overview = loadedOverview?.id === jobId ? loadedOverview : null; + const overviewRequest = useRef(0); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const [editOpen, setEditOpen] = useState(false); @@ -123,6 +125,7 @@ export function ApplicationWorkspace({ }, [blocker, confirm, t]); const load = useCallback(async () => { + const request = ++overviewRequest.current; if (!Number.isInteger(jobId) || jobId <= 0) { setOverview(null); setError({ kind: "error", message: t("workspaceInvalidLink"), retryable: false }); @@ -132,17 +135,20 @@ export function ApplicationWorkspace({ try { setError(null); setLoading(true); - setOverview(await applicationWorkspaceApi.overview(jobId)); + const next = await applicationWorkspaceApi.overview(jobId); + if (request === overviewRequest.current) setOverview(next); } catch (err) { + if (request !== overviewRequest.current) return; const status = (err as { response?: { status?: number } })?.response?.status; setError({ kind: "error", message: getApiErrorMessage(err, t("workspaceLoadFailed")), retryable: !status || status >= 500, status }); } finally { - setLoading(false); + if (request === overviewRequest.current) setLoading(false); } }, [jobId, t]); useEffect(() => { - load(); + void load(); + return () => { overviewRequest.current += 1; }; }, [load]); useEffect(() => { @@ -253,16 +259,17 @@ export function ApplicationWorkspace({ {subsections.map((sub) => )} - {section === "application" && subsection === "cv" && jobId > 0 && } - {section === "application" && subsection === "letter" && jobId > 0 && } - {section === "application" && subsection === "answers" && jobId > 0 && 0 && } + {section === "application" && subsection === "letter" && jobId > 0 && } + {section === "application" && subsection === "answers" && overview && jobId > 0 && } - {section === "application" && subsection === "submission" && jobId > 0 && } + {section === "application" && subsection === "submission" && jobId > 0 && } {section === "timeline" && subsection === "activity" && jobId > 0 && } {section === "timeline" && subsection === "correspondence" && jobId > 0 && } @@ -272,7 +279,7 @@ export function ApplicationWorkspace({ }> - + } {section === "tasks" && jobId > 0 && <> } onClick={() => setEditOpen(true)}>{t("workspaceEdit")}}>