feat(ui): redesign job header and overview

This commit is contained in:
cesnimda
2026-10-06 22:58:45 +02:00
parent 42a6e425b6
commit fadf99a3f5
41 changed files with 3779 additions and 67 deletions
+2
View File
@@ -1,5 +1,7 @@
# Redesign traceability
Job header/Overview: [P5-08a scoped inventory, preservation and evidence](../phase-5/p5-08a/README.md#preservation-impact-before-implementation) maps PRES-W01/W02/W03 entry, W04/W08/W11 links, J05/J06/J07/J09 entry and E07–E09 to the existing owner, semantic header/summary, progress keyboard access, five new unit cases and two local-browser cases. Deeper W05–W10/W12 owners remain regression dependencies; native package proof, incomplete accordion/contrast checks and manual screen-reader acceptance remain open.
Existing reports destination: [P5-07 inventory, scoped trace and evidence](../phase-5/p5-07/README.md#preservation-and-implementation-decision) maps PRES-S01/S02, E07–E09 and J05/J09 to the shared report owner, authenticated Analytics entry, period/query preservation and existing drill-downs. Dashboard flags/reports remain available; new routes do not imply new metric/provider/production acceptance.
Personal Dashboard: [P5-06 scoped trace, metric definitions and rendered evidence](../phase-5/p5-06/README.md#design-and-preservation-plan) maps PRES-S01/S02, E07–E09, W11/W12 and J05/J09 to retained report/preference/navigation behavior, eight unit regressions and two real-application browser cases. Exact values, independent failures and owner switching have scoped evidence; manual screen-reader acceptance and production evidence remain open.
+4 -1
View File
@@ -14,7 +14,7 @@ Reconciled against [P4-06](../phase-4/p4-06/README.md#opendeferred-issue-reconci
| P3-O02 command error→empty / late query results | **Addressed in P5-02**: failed search has Retry, aborted/late results are discarded after a newer query or close; focused and real-browser regressions pass | Search-state gate closed locally; manual screen-reader acceptance is separately open below |
| P5-02-D01 utility screen-reader operation | Automated axe and keyboard checks and command/notification semantic regressions pass; spoken names and live/state announcements have not been manually verified | **Open mandatory P5-15 accessibility acceptance item**. It does not block independent P5-03 Jobs work under Connor's explicit scope clarification. Operator steps and evidence request are in [P5-02](p5-02/README.md) |
| P3-O02 workspace search replacement loses `followMode` | Source-confirmed `setParams({section})` | **Blocks later slice P5-08b** adapter migration until retained-query proof |
| P4-02-D03 mobile progress scroll not focusable | Open loaded axe finding | **Blocks P5-08a affected Overview migration**, not shell; targeted G-WORKSPACE proof |
| P4-02-D03 mobile progress scroll not focusable | **Closed for the targeted strip in [P5-08a](p5-08a/README.md)**: named keyboard region, native arrow scrolling, visible shared focus and current-step semantics | Scoped G-WORKSPACE 08a gate met; does not certify deeper accordions or full screen-reader acceptance |
| P4-02-D04 literal AI context `{name}` | Open both locales | **Blocks P5-08b affected application AI labels**; correct interpolation without changing context/API |
| P4-02-D02 company-save error outside accessible modal | Open; draft retained, transient toast hidden by modal isolation | **Does not block Phase 5 shell/navigation**. Company form/Phase 7 owner; direct company-form restructuring requires durable in-modal error. P5-00 toast position does not close this |
| P4-04-O02 concurrent SQLite notification save | Open, earlier 2/10 native attempts HTTP 500 / database locked | **Separate backend reliability**; no shell/visual blocker with unchanged calls, but any increased polling/write concurrency waits for proof. Sequential passing test does not close it |
@@ -28,6 +28,9 @@ Reconciled against [P4-06](../phase-4/p4-06/README.md#opendeferred-issue-reconci
| P3-O01 analytics names/periods/populations | [P5-06](p5-06/README.md) addresses Dashboard labels; [P5-07](p5-07/README.md) reuses and revalidates the same cohorts/snapshots/samples and exact values in Analytics, independent of Dashboard flags | Dashboard/Analytics G-METRICS has scoped regression evidence; current server population/date limitations remain documented and backend metric/timezone repair remains separate |
| P5-06-O01 native language preference 500 during reads | One full-run fixture response was generic 500 while Dashboard/setup reads overlapped; exception/root cause not established. Visual setup now writes the native preference before mounting the next fixture, still requiring 204 | **Separate baseline auth/storage investigation**, not resolved by presentation. Concurrent language mutation during loading is not certified; [observation and test sequencing](p5-06/README.md#regression-protection) |
| P5-07-O01 attachment confirmation focus observation | Initial full run: unchanged light/English desktop Shift+Tab from Cancel did not retain Delete focus; existing transition `onEntered` also refocuses Cancel. Race explanation remains uninstrumented | Separate shared-dialog accessibility investigation; no provider/test change in Analytics, no closure from a passing rerun. [Evidence](p5-07/README.md#validation-observations-retained) |
| P5-08a-O01 next-task destination adapter | Existing `complete-career-profile` falls back to `checklist`, which maps to Overview without opening Profile/expanding checklist; other unsupported task aliases remain | **P5-08b navigation/entry prerequisite**. Supported prepare-CV entry is preserved/tested; do not invent a new workflow in the header slice. [Record](p5-08a/README.md#deferred-observations-and-p5-08b-boundary) |
| P5-08a-O02 menu entry contrast scan | Two complete runs caught unchanged dark Dashboard menu text during entry; direct inspection found opacity 0.0111545 before settling to 1. The original scan now waits for settled opacity and passes unchanged rules | Validation fixture synchronization only; no Dashboard styling repair or transition-wide/manual certification. Both failed summaries and scan targets remain [recorded](p5-08a/README.md#validation-observations-retained) |
| P5-08a-O03 deeper accordion duplicate ARIA IDs | Both scans have six incomplete duplicate-id-aria nodes; source confirms unchanged AccordionDetails IDs duplicate MUI-generated region IDs | **P5-08b affected deeper composition/accessibility gate**. Not a detected violation pass, not newly introduced by header/summary work; [evidence](p5-08a/README.md#accessibility-localization-and-responsive-validation) |
| F04/B06 production ingress | **Open**; repository/local checks are not deployed proof. Historical DNS/HTTPS observations do not prove actual peer/firewall boundary or current topology | **Production gate; does not block local P5 slices**. Operator evidence for actual Traefik peer, nginx trust, listeners/ports, direct/spoofed access, B04 client IP, firewall/NAT/IPv6, DNS/TLS/redirect and restart needed; no closure or credentials accessed here |
| Protected-main CI/browser runner and deployment | Unresolved remote-runner evidence; local tests pass | **Separate release gate**; feature-branch docs/UI push is not production deployment approval |
| F19 missing provider/config falls back to SQLite | Open source carryover | **Separate startup/deployment safety**, not a UI redesign dependency; no backend provider change here |
+1 -1
View File
@@ -72,7 +72,7 @@ This is where the reported incorrect margins/templates must be understood and co
| Gate | Entry condition / scope | Does not block |
|---|---|---|
| G-METRICS | Before P5-06/07: exact source/population/period/sample labels from Phase 3; browser tests for transformations, all five owner visibility flags and error vs zero. [P5-06](p5-06/README.md) supplies Dashboard presentation proof; [P5-07](p5-07/README.md) supplies native Analytics values, period/Back, supported query links and independent error evidence | Shell, Jobs |
| G-WORKSPACE | Before P5-08a: reproduce/protect mobile progress-scroll accessibility. Before 08b: section/query/`followMode` retention, draft/AI review guards and all legacy mappings; G-PACKAGE also required | Table, independent charts |
| G-WORKSPACE | P5-08a's targeted progress-scroll gate is satisfied by [named-region/native arrow/focus evidence](p5-08a/README.md#accessibility-localization-and-responsive-validation). Before 08b: section/query/`followMode` retention, next-task aliases, draft/AI review guards, deeper accordion ARIA IDs and all legacy mappings; G-PACKAGE also required | Table, independent charts |
| G-CV-BEHAVIOR | Before each CV slice: source/reset/item override, browser history restore, reorder alternatives, save failure and AI accept/reject regression required by its PRES rows, in addition to G-CV-REV/RENDER | Non-editor pages; planning/fixture specification may proceed |
| G-RELEASE | F04 deployed peer/listeners/ports/firewall/NAT/IPv6/DNS/TLS/restarts; production CI/browser runner, providers, backup/restore/rollback and relevant billing/configuration evidence | Local feature-branch UI planning/testing; never grants production approval |
+1 -1
View File
@@ -1,6 +1,6 @@
# Phase 5 — Representative screens / major UI redesign plan
**Current successor: [P5-07 existing-report Analytics destination](p5-07/README.md).** Insights → Analytics supplies deeper existing-report reading through the shared Dashboard owner, exact values and explicit window/current/all-time scope. [P5-06](p5-06/README.md) retains Dashboard reports, attention and all five owner-local choices. The [P5-04b integration](p5-04b/README.md) and [query-readiness checkpoint](p5-04b-readiness/README.md) remain authoritative for Jobs. P5-02 manual screen-reader acceptance remains mandatory for P5-15. P2-D01, F04, incomplete accessibility checks, native touch verification, Board scaling and recorded baseline timing/preference-save observations remain open. P5-08a, Phase 8 and B07 have not started.
**Current successor: [P5-08a Job header and Overview](p5-08a/README.md).** The existing workspace owner now uses shared heading/section/state foundations, recorded date summary, named actions and keyboard-scrollable progress. Five sections, six deeper disclosures, routes and domain owners remain. P4-02-D03 closes for the targeted strip; native package proof still blocks P5-08b. [P5-07 Analytics](p5-07/README.md), [P5-06 Dashboard](p5-06/README.md) and [P5-04b Jobs integration](p5-04b/README.md) remain authoritative for their areas. P5-02 manual screen-reader acceptance remains mandatory for P5-15. P2-D01, F04, incomplete accessibility checks, native touch, Board scaling and recorded timing/preference-save observations remain open. P5-08b, Phase 8 and B07 have not started.
Historical planning checkpoint: source `2a2f9e48052c562ef599825458d0d3a6f9f9fc61`, inspected on 2026-09-13 on `codex/ui-redesign-phase4`. That checkpoint was documentation-only; the successor record above owns implementation/validation status.
+155
View File
@@ -0,0 +1,155 @@
# P5-08a — Job header and Overview
Completed from `42a6e425b6e4` on `codex/ui-redesign-phase4`. Header/Overview only; P5-08b and B07 are not included. Local scoped acceptance is satisfied; this is not production or complete screen-reader certification.
## Existing owners and data inventory
`ApplicationWorkspacePage` owns the aggregate read, edit dialog, five section keys, return context and dirty-navigation guard. `/jobs/:id` and `/applications/:id` continue using that owner. Overview owns six existing accordions: job details, checklist, calendar, timeline, documents and correspondence. Analysis, CV, cover-letter/drafts and interview children retain their existing loading/mutation owners.
The authenticated workspace endpoint resolves the principal and owner-loads the job. The service reads job/company/source, attached CV, attachment count, saved AI history and eight recent JobEvents. Its existing checklist read seeds system tasks; this historical side effect is not new client generation or changed by this slice.
Available: role/company/location/salary, literal status and pipeline metadata, saved/applied/deadline/follow-up dates, listing URL/source/country, original/translated description, tags/notes, selected CV identity, cover-letter readiness, document and saved-AI counts, checklist progress and server-selected next pending task. The next task remains the server checklist's recommendation, not a new client workflow engine.
Not in the aggregate: favourite/priority, structured work arrangement/employment type, response/interview/offer dates, actual match score/freshness or submitted-package identity. Existing edit/deeper sections remain the entry for their supported data. No speculative fields, additional match request, AI generation, package capture, rendering or backend contract is added to fill the design.
## Preservation impact before implementation
| Contracts | Retained behaviour | Bounded change | Required evidence |
|---|---|---|---|
| PRES-W01, J05/J06/J07/J09 entry | Five section keys/aliases, edit/details, full-page/optional overlay props, list return/filter/focus, dirty cancel/discard | Local header and Overview presentation | Existing route/overlay and native smoke/Jobs Back suites |
| PRES-W02 | Literal/custom status, deterministic progress, dates/details/description, server next task, recent activity | Header hierarchy, summary, keyboard-scrollable progress | New progress/custom-status/read-retry fixtures; native responsive workflow |
| PRES-W03 entry | Explicit analysis/match/strategy/review owners | Retain Analysis and saved-AI entry | Intelligence/workflow suites; no automatic generation |
| PRES-W04/W08/W11 links | Checklist, documents, calendar and their owners | Preserve existing Overview accordions | Checklist/attachments/calendar and native smoke |
| PRES-W05/W06/W07/W09/W10 | CV, drafts, immutable packages, interview and follow-up owners | Unchanged children | Full frontend/backend regression; package proof remains blocked |
| PRES-W12 adjacent dependency | Existing asynchronous operation/progress/notification owners | No producer, polling or operation change | Existing operation/profile tests; no new provider/worker acceptance |
| PRES-E07/E08/E09 | Authenticated routing, semantic theme/focus, EN/NB, identity isolation | Shared PageHeader/PageSection/ViewStateNotice consumption | Themes/locales/widths, missing/foreign resource and security suites |
G-WORKSPACE's P4-02-D03 is reproduced in the historical phone scans: the progress strip scrolls horizontally but has no keyboard scroll entry. Add a named focusable scroll region at that strip only. Stages stay noninteractive. Custom status must not imply completion of every standard stage.
## Open gates
G-PACKAGE/P2-D01 remains open. Packages/tailoring/deeper section composition are untouched; native preservation proof is still required before P5-08b. F04, P5-02 manual screen-reader acceptance, incomplete axe checks, preference-save 500, Board scaling/native touch and P5-07-O01 are retained separately.
## Header hierarchy and action decisions
Reuse PageHeader below the existing shell h1: role is h2, company is its description, literal/localized status and location/deadline sit immediately below. Edit is a named outlined button invoking the unchanged EditJobDialog; the original advert is a labelled external link with the existing new-tab/no-opener relationship. The existing Back control, optional full-page link and five scrollable tabs remain. Long titles/company/custom status wrap; actions wrap rather than compress or disappear. Source and salary remain in Job details instead of crowding the header.
Status editing still happens through the existing edit dialog; reviewed message-status suggestions remain in their owner. No new status endpoint, deletion/menu workflow, favourite state or header-only package action is introduced. Existing delete/archive/trash operations remain in their existing Jobs/Trash owners, not silently removed or reimplemented here.
## Overview hierarchy and retained domain content
1. A compact Application information definition list presents recorded saved/applied/deadline/follow-up dates. Absent values are `—`, not inferred dates. UI language selects the existing EN/NB date formatting; stored timestamps/timezone semantics are unchanged.
2. The server checklist's next pending task remains the recommendation and existing section action. Its label/description use existing system-key translations or literal user task text. No client stage/wizard or automatic AI content is added.
3. Application preparation retains all five existing entries and counts: selected/tailored CV, cover letter, documents, saved AI suggestions and checklist. Native MUI Buttons replace manually keyboard-wired Paper buttons, reuse shared focus treatment and retain the same handlers. Incomplete preparation text uses readable secondary text rather than disabled-colour styling.
4. Recent activity retains all eight server-provided events and Refresh; narrow screens stack dates below details. Backend event type strings remain literal as before; no new event stream or localization inference.
5. All six existing accordions and domain children remain in the same owner/order. JobDetailsSection's salary/source/country/tags/notes/original/translated description stays intact. Calendar, timeline, uploads/correspondence, CV/package, interview and AI review owners are unchanged. Dates are deliberately repeated in the full disclosure for low-risk compatibility; deeper consolidation belongs to P5-08b after its gates.
The progress strip now has a named keyboard-focusable scroll region using the shared focus-visible foundation. Arrow keys scroll natively; individual stages stay noninteractive and the current standard stage has `aria-current="step"`. Unknown/custom status remains literal and shows that no standard position exists. The prior `Number.MAX_SAFE_INTEGER` fallback falsely coloured all standard stages complete; no backend/custom status values or standard movement semantics change.
ViewStateNotice supplies initial load/completion and explicit error semantics. Retry repeats only the existing aggregate GET for transport/server failure; invalid, unauthorized, forbidden and missing resources offer Back without a futile retry. Existing mounted section/draft owners are not replaced by a new resource framework or remounted merely on refresh. Historical failed-load handling already replaces the workspace with an error; this slice does not redesign draft recovery.
## Regression protection and bounded fixes
Before implementation the new fixture run had 7 inherited passes and 3 expected-to-reproduce failures: no named scroll region, custom standard-pipeline overstatement and missing Retry. After implementation that suite has 12 passes: the same 7 inherited cases and 5 new cases for progress/current-stage semantics, literal custom status, read retry/no mutation, supported next-task/CV entry and recorded/unknown dates.
The two new native-browser cases use disposable owned records, preserve five tabs/aliases, test edit Escape/focus return, original-link behavior, no client mutation on open, trusted owner read denial, dates/custom status/sparse records and native progress scrolling/focus. A controlled local transport fixture tests loading→503→Retry→404 explicitly; it is not native missing-resource or ownership proof, which remain covered separately by the real API and inherited workspace smoke.
The first new browser attempt used a nonexistent theme-preference endpoint in the fixture (404). Corrected the fixture to the existing local theme owner and native language preference endpoint; no application endpoint or test expectation was weakened. The focused rerun passed both cases. Rendered review then found an undefined spacing variable introduced by this slice; corrected it to existing `--app-section-gap` before final complete validation. One new unit fixture used unsupported Testing Library `exact` typing; removed that fixture-only option before the final build.
## Deferred observations and P5-08b boundary
- **P5-08a-O01, existing next-task adapter limitation:** server `complete-career-profile` has no section, so its overview fallback is `checklist`. Existing `go` writes that key and `workspaceSection` maps it back to Overview; it does not open the canonical Career Profile or expand the checklist automatically. Unknown task destinations such as `portfolio`/`notes` also lack current workspace adapters. This slice preserves the server recommendation and existing handler rather than invent a workflow. Supported `prepare-cv → cv` is tested. Resolve intended aliases/entry behavior in the authorized P5-08b adapter work, not by claiming all task buttons have a native destination today.
- Existing `followMode` loss from `setParams({section})` and literal AI `{name}` remain P5-08b gates. No route/query adapter or deeper section migration occurred.
- Native package capture/tailoring success plus immutable archived-binary/source-mutation proof is still absent. P5-08b remains **NO-GO** until G-PACKAGE and its own query/draft/AI-label prerequisites pass.
- Manual screen-reader spoken names/live announcements, real touch and incomplete automated contrast checks remain explicit acceptance gaps. No screenshot or axe pass certifies them.
- **P5-08a-O03, existing deeper accordion ARIA IDs:** the unchanged OverviewDetails gives each AccordionSummary `aria-controls="<panel>-content"` and repeats that ID on AccordionDetails. The installed MUI Accordion also creates its region with the Summary's `aria-controls` ID. Both nodes therefore share each of six content IDs. The scoped axe scans report this under `incomplete`, not `violations`; source inspection confirms the duplication. Deeper accordion/section accessibility must address this in P5-08b's authorized composition work. It is not a newly introduced Header/Overview-summary defect and was not silently repaired here.
- F04, P2-D01, P5-06-O01 preference-save 500, P5-07-O01 confirmation-focus observation, Board scaling/native touch and other pre-existing debt remain open. No provider, schema, backend, shell, CI, renderer or dependency changes were made.
## Files and scope review
- `job-tracker-ui/src/views/ApplicationWorkspacePage.tsx`: existing header/Overview owner, shared page/state primitives, native preparation buttons, date summary and progress accessibility. No replacement section or new routing owner.
- `job-tracker-ui/src/i18n/translations.ts`: two labels in each existing language for application preparation and a custom status without a standard pipeline position.
- `job-tracker-ui/src/application-workspace-overlay.test.tsx`: five behavioral regressions alongside seven retained routing/overlay/dirty-navigation cases.
- `job-tracker-ui/e2e/job-overview-presentation.spec.ts`: two local-browser regressions with native owned records plus controlled failed-read states.
- `job-tracker-ui/e2e/dashboard-presentation.spec.ts`: two lines synchronize the existing menu-contrast scan with completed entry opacity; justification and retained failure evidence below. No new case, disabled rule or altered contrast assertion.
- This implementation record, sanitized evidence, current Phase 5 index/readiness/blocker links, preservation trace index and TODO status.
Dashboard, theme, AppShell, App routes/providers, backend, CI and dependencies are unchanged. Shared PageHeader/PageSection/ViewStateNotice and existing theme tokens are consumed, not copied. No native package/archive operation, PDF renderer, AI write, delete/archive flow or query adapter was changed. Existing underlying controls remain available in their current disclosures/owners.
## Validation observations retained
**P5-08a-O02 — inherited Dashboard menu contrast observation:** two complete pre-synchronization browser runs each had 82 actual passes, unchanged P2-D01 expected failure and one unexpected failure in the existing Dashboard customization-menu axe assertion. Dark/English menu text was measured at 3.77:1 for the focused item and 3.87:1 for other items against the 4.5:1 requirement. The [initial](evidence/initial-browser-summary.json) and [second](evidence/second-browser-summary.json) summaries preserve both outcomes; the [sanitized second scan](evidence/dashboard-entry-axe-summary.json) retains all five affected targets/ratios without raw HTML.
Direct read-only browser inspection confirmed entry opacity `0.0111545`, the existing 305ms opacity/203ms transform transition, and settled opacity `1` with `rgb(234,234,242)` text on `rgb(28,28,36)`. This establishes that focus alone is not proof of settled contrast. The exact opacity during the earlier axe computation was not instrumented, so the timing explanation is supported rather than retrospectively proven. To obtain a meaningful stable-state baseline, the existing fixture now polls the semantic menu's parent computed opacity to `1` before the original axe scan. This bounded validation synchronization is the only change outside the workspace/i18n owners. Dashboard styling, keyboard/focus assertions, rules, contrast assertion, expected-failure annotation and retry configuration are unchanged. No product transition, Dashboard redesign or unrelated defect repair is included; transition-wide and manual screen-reader acceptance are not certified by a settled-state pass.
No raw authentication cookies, CSRF/session context, trace archives or response bodies are committed as evidence. The browser tests only call the disposable local API and abort external page requests; no production verification or provider call is claimed.
## Accessibility, localization and responsive validation
The existing shell h1 is retained; the role is an h2 and Overview sections use shared h2 headings with the next task at h3. Buttons/links have visible names independent of tooltips. Status uses literal/localized text plus the existing semantic color, and custom text is never translated into a fabricated standard stage. The keyboard-scrollable progress region has a shared ≥3px focus outline and native arrow scrolling; its stages are not invented controls. Preparation buttons retain normal Enter/Space activation rather than custom keyboard handlers. Edit retains its existing Escape and focus-return behavior.
Native fixture captures cover all four combinations of light/dark and English/Bokmål at 1440×900, 768×1024 and 390×844, with additional dark/Bokmål 320×900 and 1920×900 cases. Long Norwegian role/company names, date labels and wrapping actions are deliberate fixtures. On phones the summary stacks, existing tabs/progress remain their own horizontal scroll surfaces and the page itself does not horizontally overflow. Reduced-motion behavior is inherited from the shared foundation; no new animation is introduced.
The two English-phone axe scans use the existing WCAG A/AA tags without disabling rules. The [final sanitized scan summary](evidence/workspace-axe-summary.json) has zero detected violations in each, but **five incomplete color-contrast nodes and six incomplete duplicate-id-aria nodes per scan**. Contrast could not be determined for the invisible notification badge and four off-screen/partially obscured progress captions after horizontal scrolling; these are not contrast passes. The six duplicate IDs belong to unchanged deeper accordions (P5-08a-O03 above), not falsely accepted as harmless. Scans do not certify untested sections, states/locales or spoken names/live announcements. Broader manual screen-reader acceptance remains mandatory for P5-15. Native touch, exhaustive zoom/forced-colors and calendar/DST correctness are not certified by these fixtures. The optional overlay still has its existing automated routing/dirty-navigation protection; no production quick-preview workflow is claimed.
Direct computer-use keyboard review used an existing disposable admin-owned CSV fixture job: Enter on its Jobs row opens the workspace, Enter on Edit opens the existing dialog, Escape restores Edit focus, Tab reaches the named progress region and then the Overview tab. At the requested 390×844 phone override, the actual document width/scroll width were both 375px (15px scrollbar); Right moved the progress strip to `scrollLeft=40` with its 3px dark-theme outline visible. [Direct keyboard screenshot](evidence/job-manual-progress-dark-en.png) records that state. No edit was saved. The subsequent direct Browser Back attempt overlapped the disposable API's teardown and could not validate row restoration; no manual Back pass is claimed. The four inherited Table-density native cases independently retain Browser Back/focus assertions in the complete suite. The temporary tab was closed and viewport override reset. This is direct browser/keyboard review, not a spoken screen-reader session.
### Rendered evidence
The final [visual manifest](evidence/visual-manifest.json) records native versus controlled fixtures, viewport/full-page dimensions, capture timestamps and reviewed byte hashes. All 23 scripted workspace PNGs were individually inspected after the final application change, with one additional direct computer-use keyboard capture. These are synthetic owned records against the production UI export/disposable API, not production accounts or a live deployment.
| State / representative evidence | Rendered review |
|---|---|
| Desktop [light EN](evidence/job-overview-desktop-light-en.png), [dark EN](evidence/job-overview-desktop-dark-en.png), [light Bokmål](evidence/job-overview-desktop-light-nb.png), [dark Bokmål](evidence/job-overview-desktop-dark-nb.png) | Role/company hierarchy, named actions, literal status, recorded dates, compact preparation entries, retained disclosures and semantic surfaces |
| Phone [light EN](evidence/job-overview-phone-light-en.png), [dark EN](evidence/job-overview-phone-dark-en.png), [light Bokmål](evidence/job-overview-phone-light-nb.png), [dark Bokmål](evidence/job-overview-phone-dark-nb.png) | 390×844; long role/company/action text wraps, summary stacks and only the existing tab/progress strips scroll horizontally |
| Tablet [light EN](evidence/job-overview-tablet-light-en.png), [dark EN](evidence/job-overview-tablet-dark-en.png), [light Bokmål](evidence/job-overview-tablet-light-nb.png), [dark Bokmål](evidence/job-overview-tablet-dark-nb.png) | 768×1024; actions deliberately wrap below long Bokmål identity, two-column dates/preparation and usable section navigation |
| [Minimum 320](evidence/job-overview-320-dark-nb.png), [wide 1920](evidence/job-overview-1920-dark-nb.png) | 320×900 and 1920×900 dark/Bokmål; bounded page width, multiline advert action, no document overflow; wide capture retains keyboard focus from the preceding check |
| Progress [light EN](evidence/job-progress-focus-light-en.png), [dark EN](evidence/job-progress-focus-dark-en.png), [light Bokmål](evidence/job-progress-focus-light-nb.png), [dark Bokmål](evidence/job-progress-focus-dark-nb.png) | Shared outline is visible/not clipped; edge stage text is naturally clipped only inside the keyboard-scrolled region, not removed |
| [Custom status](evidence/job-custom-status-dark-nb.png), [sparse job](evidence/job-sparse-dark-nb.png) | Native alias entry, literal custom text without false standard completion; unknown dates stay `—`, absent listing URL stays absent |
| [Loading](evidence/job-loading.png), [503 with Retry](evidence/job-load-error.png), [unavailable without Retry](evidence/job-unavailable.png) | Controlled local transport, genuine progress/error messages, Back retained; no placeholder metrics or false empty-state success |
Status and dates remain textually understandable without color; there is no new match card because the aggregate has no match score. Full-page phone captures are intentionally longer because all five preparation actions and six existing disclosures remain available. This does not claim new section navigation or package functionality. Exact labels/values are also checked against DOM/native responses; a raster alone is not proof of text accuracy, accessibility or historical date semantics.
## Compatibility and rollback
All existing routes/provider boundaries are unchanged, including `/jobs/:id`, `/applications/:id` and all five current section keys. Existing aggregate read cadence and child persistence owners remain; Retry adds only an explicitly invoked repetition of that read. Header opening does not invoke AI/render/create/status writes. The new UI requires no migration or deployment setting. Rollback restores the local header/Overview composition together, without changing saved data, route adapters or immutable document/archive owners. Keep the progress accessibility and behavioral evidence when evaluating any later composition change; rolling back presentation is not a reason to restore the inaccessible strip.
## Validation commands and baseline
Inherited: 934 backend, 403 frontend, 81 actual browser passes plus unchanged P2-D01 expected failure; rate-limit 9, ingress 6, PDF admission 1, configuration 3. Commands run through `rtk proxy`; npm commands use `job-tracker-ui`. General browser and API security suites use separate disposable API lifetimes sequentially. Browser configuration uses `DOTNET_ROOT=C:/Users/Connor/.dotnet`, `DOTNET_HOST_PATH=C:/Users/Connor/.dotnet/dotnet.exe`, `PLAYWRIGHT_STATIC_EXPORT=true`, `PLAYWRIGHT_PREBUILT_EXPORT=true`; the production export uses `NEXT_PUBLIC_API_BASE_URL=http://localhost:5302/api`, `CI=false`. No configured retry or baseline annotation is changed.
| Check | Command after `rtk proxy` | Final-candidate result |
|---|---|---|
| Backend Release tests | `C:/Users/Connor/.dotnet/dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj -c Release --no-restore --logger 'console;verbosity=minimal'` | 934 passed, zero failed/skipped; unchanged |
| Frontend | `npm.cmd test -- --runInBand --silent` | 408 passed in 80 suites, zero failed/skipped; +5 workspace cases, no removal; final rerun 129.325s |
| TypeScript | `npm.cmd run typecheck` | Passed; historical TS2571 is not current |
| New E2E fixture typing | `npm.cmd exec tsc -- --noEmit --incremental false -p ../.artifacts/p5-08a-e2e-tsconfig.json` | Passed; ignored local config, no compiler/CI change |
| Lint | `npm.cmd run lint` | Passed, zero warnings |
| Frontend production export | `npm.cmd run build` | Passed after the final spacing and fixture-typing correction |
| API Release build | `C:/Users/Connor/.dotnet/dotnet.exe build JobTrackerApi/JobTrackerApi.csproj -c Release --no-restore -warnaserror` | Passed, zero warnings/errors |
| Effective Compose/config | `python scripts/test-ingress.py` | 3 passed; unchanged, not deployed ingress proof |
| Supply-chain fixtures | `python scripts/test-supply-chain.py` | 3 passed; unchanged |
| Ollama evaluation fixtures | `python scripts/test-ollama-evaluation.py` | 5 passed; unchanged |
| Extension capture URL | `node --test browser-extension/tests/capture-url.test.mjs` | 3 passed; unchanged |
These checks ran after the last application change and the final menu-fixture synchronization; documentation-only acceptance updates do not change the tested application. The first two complete browser runs are explicitly not accepted as clean runs: each had 82 actual passes, one unchanged expected failure and one unexpected inherited Dashboard contrast failure. New workspace cases passed in those runs; no existing failure is hidden behind that fact.
| Remaining check | Command after `rtk proxy` | Final result |
|---|---|---|
| Complete browser | `npm.cmd run test:e2e -- --reporter=line,json` | **83 actual passes**: 81 inherited +2 new; unchanged P2-D01 expected failure, zero unexpected/skipped/retries; 11m09s |
| Rate-limit | `npm.cmd run test:rate-limits -- --reporter=line` | 9 passed, unchanged |
| Ingress | `npm.cmd exec playwright test -- --config=playwright.ingress.config.ts --reporter=line` | 6 passed, unchanged; does not close F04 |
| PDF admission | `npm.cmd run test:pdf-admission -- --reporter=line` | 1 passed, unchanged |
| Secret scan | `python scripts/supply-chain.py scan-secrets` | Passed on staged final candidate; no high-confidence credential patterns |
| Whitespace | `git diff --check` and `git diff --cached --check` | Passed; normal LF/CRLF notices are not failures |
| Documentation/evidence | `python .artifacts/p4-04-check-doc-links.py` plus local JSON/PNG hash/dimension/outcome assertions | 141 current/root Markdown files, zero missing local targets; 24 inspected PNGs, 84 distinct case outcomes, zero retries/skips |
The [final browser summary](evidence/final-browser-summary.json) separates all 84 outcomes. Playwright's “84 passed” includes the annotated failing package case; this report counts it as one expected failure, not actual success. No existing case/rule/assertion was removed, skipped or weakened. The only inherited fixture adjustment is the explicitly justified settled-state wait, not a generic retry or annotation. Final screenshots were retained before specialized API suites replaced temporary output, and reviewed hashes match the committed PNGs.
The final scope/security review confirms that principal/ownership/session checks, outbound HTTP protections, rate partitions, PDF admission, CSV safety and ingress configuration are untouched. Existing security suites and the full backend suite remain healthy; local tests are not production F04 evidence. Only the three application files listed above changed. The extra Dashboard file is a validation fixture, with its necessity recorded rather than presented as Job-workspace implementation. No test/CI/package baseline configuration was altered.
## Acceptance and stopping point
**P5-08a accepted within its approved local presentation/preservation scope.** Job identity/status/dates/actions and all existing children remain available. Both aliases/five section keys, native missing/foreign handling, dirty navigation and Jobs Browser Back contracts retain regression coverage. Light/dark, English/Bokmål and desktop/tablet/phone/minimum-width rendered review is complete; keyboard progress access resolves the specifically assigned P4-02-D03 gate. This does not certify every protected workflow, provider, touch or screen-reader state.
**P5-08b remains NO-GO:** G-PACKAGE native capture/archive/historical-binary proof is still missing. Its section/query/`followMode` retention, draft/AI review guards, literal AI `{name}` correction and legacy mappings also require their own prerequisite evidence. P5-08a-O01's next-task adapter limitation and O03's existing accordion IDs must be addressed at the approved deeper navigation/composition boundary. No package/tailoring restructure or P5-08b implementation occurred. F04 and the other carried operational/accessibility debt remain explicitly open. Stop here; do not begin P5-08b or B07 automatically.
@@ -0,0 +1,116 @@
{
"observation": "P5-08a-O02; second pre-synchronization complete run",
"scans": [
{
"name": "dashboard-dark-menu-axe",
"violations": [
{
"id": "color-contrast",
"impact": "serious",
"nodes": [
{
"target": [
".Mui-focusVisible"
],
"checks": [
{
"id": "color-contrast",
"message": "Element has insufficient color contrast of 3.77 (foreground color: #787882, background color: #1e1e29, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1",
"data": {
"fgColor": "#787882",
"bgColor": "#1e1e29",
"contrastRatio": 3.77,
"fontSize": "10.5pt (14px)",
"fontWeight": "normal",
"messageKey": null,
"expectedContrastRatio": "4.5:1"
}
}
]
},
{
"target": [
".MuiMenuItem-root.MuiMenuItem-gutters.css-qy8v2z:nth-child(2)"
],
"checks": [
{
"id": "color-contrast",
"message": "Element has insufficient color contrast of 3.87 (foreground color: #727278, background color: #131318, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1",
"data": {
"fgColor": "#727278",
"bgColor": "#131318",
"contrastRatio": 3.87,
"fontSize": "10.5pt (14px)",
"fontWeight": "normal",
"messageKey": null,
"expectedContrastRatio": "4.5:1"
}
}
]
},
{
"target": [
".MuiMenuItem-root.MuiMenuItem-gutters.css-qy8v2z:nth-child(3)"
],
"checks": [
{
"id": "color-contrast",
"message": "Element has insufficient color contrast of 3.87 (foreground color: #727278, background color: #131318, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1",
"data": {
"fgColor": "#727278",
"bgColor": "#131318",
"contrastRatio": 3.87,
"fontSize": "10.5pt (14px)",
"fontWeight": "normal",
"messageKey": null,
"expectedContrastRatio": "4.5:1"
}
}
]
},
{
"target": [
".MuiMenuItem-root.MuiMenuItem-gutters.css-qy8v2z:nth-child(4)"
],
"checks": [
{
"id": "color-contrast",
"message": "Element has insufficient color contrast of 3.87 (foreground color: #727278, background color: #131318, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1",
"data": {
"fgColor": "#727278",
"bgColor": "#131318",
"contrastRatio": 3.87,
"fontSize": "10.5pt (14px)",
"fontWeight": "normal",
"messageKey": null,
"expectedContrastRatio": "4.5:1"
}
}
]
},
{
"target": [
".MuiMenuItem-root.MuiMenuItem-gutters.css-qy8v2z:nth-child(5)"
],
"checks": [
{
"id": "color-contrast",
"message": "Element has insufficient color contrast of 3.87 (foreground color: #727278, background color: #131318, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1",
"data": {
"fgColor": "#727278",
"bgColor": "#131318",
"contrastRatio": 3.87,
"fontSize": "10.5pt (14px)",
"fontWeight": "normal",
"messageKey": null,
"expectedContrastRatio": "4.5:1"
}
}
]
}
]
}
]
}
]
}
@@ -0,0 +1,856 @@
{
"sourceBase": "42a6e425b6e4765c2b41c651b3c7ec8a3235d9f2",
"statistics": {
"startTime": "2026-10-06T20:43:18.295Z",
"duration": 669037.368,
"expected": 84,
"skipped": 0,
"unexpected": 0,
"flaky": 0
},
"actualPasses": 83,
"expectedFailures": 1,
"unexpected": [],
"cases": [
{
"file": "accessibility.spec.ts",
"title": "public entry points meet automated WCAG A and AA checks",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "accessibility.spec.ts",
"title": "primary authenticated workspaces meet automated WCAG A and AA checks",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "retained profile request from A cannot write using B cookies and fresh B CSRF",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "missing and forged session contexts fail closed while same-account profile saves work",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "crafted CV identifiers cannot cross read update delete or job-association ownership",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "[P5-00 PRES-E05 E07 E08] account switch disposes open command, account and notification surfaces",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "two tabs discard A's private form before B can use it, including Back navigation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "analytics-presentation.spec.ts",
"title": "[PRES-S01 S02 E07 E08 E09 J05 J09] Analytics native reports, direct entry, period, drill-down and owner isolation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "analytics-presentation.spec.ts",
"title": "[PRES-S01 E08] Analytics independent loading and failure states never masquerade as empty reports",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "board-presentation.spec.ts",
"title": "[PRES-J07 J05 W01 W02 E07 E08 E09] Board query membership matches Table and preserves moves, focus and route",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "board-presentation.spec.ts",
"title": "[PRES-J05 J07 E07 W01] legacy Board replaces history and preserves query, hash and route state; default Jobs stays Table",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "board-presentation.spec.ts",
"title": "[PRES-J07 E08] Board loading, retryable error and fully empty columns",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] light/en preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] dark/nb preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] light/nb preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] dark/en preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "csv-export.spec.ts",
"title": "CSV export has safe cells and headers, and browser download preserves its UTF-8 bytes",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "dashboard-presentation.spec.ts",
"title": "[PRES-S01 S02 E07 E08 E09] Dashboard native data, visibility and responsive presentation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "dashboard-presentation.spec.ts",
"title": "[PRES-S01 E08] Dashboard loading and independent failed resources retain retry and exact values",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "job-discovery.spec.ts",
"title": "discovery remains usable across widths and opens the reviewed import",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "job-overview-presentation.spec.ts",
"title": "[PRES-W01 W02 W03 J05 E08 E09] native Job header/Overview retains actions, custom status, aliases and mobile progress keyboard access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "job-overview-presentation.spec.ts",
"title": "[PRES-W01 W02 E08] Job Overview loading, failed read, retry and unavailable resource remain explicit",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "kanban-theme.spec.ts",
"title": "kanban columns use dark theme surfaces",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] light/en/1440 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] dark/nb/1440 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] light/nb/390 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] dark/en/390 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-C01 PRES-C06 PRES-C07 PRES-C10] mobile CV override persists without changing the canonical profile or sibling CV",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-W01 PRES-W05 PRES-W06 PRES-W07 PRES-W08] legacy workspace preserves frozen documents after current sources change",
"expectedStatus": "failed",
"outcome": "expected",
"actualStatuses": [
"failed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-S03 PRES-W11] responsive settings navigation keeps rule edits and persists notification preferences",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-S03] rule changes persist through the real API",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-E07 PRES-W01 PRES-J04] keyboard command navigation and legacy workspace entry preserve the selected job",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-A01 PRES-S04 PRES-E07] non-admin navigation cannot grant API privileges or unlock CV AI",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/en/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/en/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/nb/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/nb/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J05 PRES-J07] light loaded status semantics, navigation and command focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-C06 PRES-C07 PRES-C08 PRES-C09 PRES-C11 PRES-S03] light loaded CV tabs, selects and settings",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/en/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/en/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/nb/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/nb/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J05 PRES-J07] dark loaded status semantics, navigation and command focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-C06 PRES-C07 PRES-C08 PRES-C09 PRES-C11 PRES-S03] dark loaded CV tabs, selects and settings",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] light/en Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] light/nb Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] dark/en Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] dark/nb Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] light-en mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] light-en command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] light-en utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] light-en real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] light-en grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E09 C06 S02 S03] light-en dirty editor survives shell utilities and cancel",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] light-nb mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] light-nb command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] light-nb utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] light-nb real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] light-nb grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] dark-en mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] dark-en command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] dark-en utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] dark-en real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] dark-en grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] dark-nb mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] dark-nb command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] dark-nb utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] dark-nb real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] dark-nb grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E09 C06 S02 S03] dark-nb dirty editor survives shell utilities and cancel",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "API liveness and database readiness are separate public probes",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "public plans stay honest, responsive and keyboard accessible",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "login establishes an authenticated session",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "a Free account keeps manual work available while AI actions stay honestly locked",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "a saved job can be created through the reviewed UI flow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "the dedicated application workspace survives deep links, long data and unsaved navigation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "Career Workspace loads from the authenticated application shell",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "a public CV renders anonymously and downloads as PDF",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "the Code template exports a long structured CV as searchable multi-page PDF",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] light/en table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] light/nb table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] dark/en table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] dark/nb table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
}
]
}
@@ -0,0 +1,858 @@
{
"sourceBase": "42a6e425b6e4765c2b41c651b3c7ec8a3235d9f2",
"statistics": {
"startTime": "2026-10-06T17:41:35.925Z",
"duration": 721346.9219999999,
"expected": 83,
"skipped": 0,
"unexpected": 1,
"flaky": 0
},
"actualPasses": 82,
"expectedFailures": 1,
"unexpected": [
"[PRES-S01 S02 E07 E08 E09] Dashboard native data, visibility and responsive presentation"
],
"cases": [
{
"file": "accessibility.spec.ts",
"title": "public entry points meet automated WCAG A and AA checks",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "accessibility.spec.ts",
"title": "primary authenticated workspaces meet automated WCAG A and AA checks",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "retained profile request from A cannot write using B cookies and fresh B CSRF",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "missing and forged session contexts fail closed while same-account profile saves work",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "crafted CV identifiers cannot cross read update delete or job-association ownership",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "[P5-00 PRES-E05 E07 E08] account switch disposes open command, account and notification surfaces",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "two tabs discard A's private form before B can use it, including Back navigation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "analytics-presentation.spec.ts",
"title": "[PRES-S01 S02 E07 E08 E09 J05 J09] Analytics native reports, direct entry, period, drill-down and owner isolation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "analytics-presentation.spec.ts",
"title": "[PRES-S01 E08] Analytics independent loading and failure states never masquerade as empty reports",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "board-presentation.spec.ts",
"title": "[PRES-J07 J05 W01 W02 E07 E08 E09] Board query membership matches Table and preserves moves, focus and route",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "board-presentation.spec.ts",
"title": "[PRES-J05 J07 E07 W01] legacy Board replaces history and preserves query, hash and route state; default Jobs stays Table",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "board-presentation.spec.ts",
"title": "[PRES-J07 E08] Board loading, retryable error and fully empty columns",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] light/en preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] dark/nb preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] light/nb preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] dark/en preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "csv-export.spec.ts",
"title": "CSV export has safe cells and headers, and browser download preserves its UTF-8 bytes",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "dashboard-presentation.spec.ts",
"title": "[PRES-S01 S02 E07 E08 E09] Dashboard native data, visibility and responsive presentation",
"expectedStatus": "passed",
"outcome": "unexpected",
"actualStatuses": [
"failed"
],
"attempts": 1
},
{
"file": "dashboard-presentation.spec.ts",
"title": "[PRES-S01 E08] Dashboard loading and independent failed resources retain retry and exact values",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "job-discovery.spec.ts",
"title": "discovery remains usable across widths and opens the reviewed import",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "job-overview-presentation.spec.ts",
"title": "[PRES-W01 W02 W03 J05 E08 E09] native Job header/Overview retains actions, custom status, aliases and mobile progress keyboard access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "job-overview-presentation.spec.ts",
"title": "[PRES-W01 W02 E08] Job Overview loading, failed read, retry and unavailable resource remain explicit",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "kanban-theme.spec.ts",
"title": "kanban columns use dark theme surfaces",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] light/en/1440 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] dark/nb/1440 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] light/nb/390 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] dark/en/390 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-C01 PRES-C06 PRES-C07 PRES-C10] mobile CV override persists without changing the canonical profile or sibling CV",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-W01 PRES-W05 PRES-W06 PRES-W07 PRES-W08] legacy workspace preserves frozen documents after current sources change",
"expectedStatus": "failed",
"outcome": "expected",
"actualStatuses": [
"failed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-S03 PRES-W11] responsive settings navigation keeps rule edits and persists notification preferences",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-S03] rule changes persist through the real API",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-E07 PRES-W01 PRES-J04] keyboard command navigation and legacy workspace entry preserve the selected job",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-A01 PRES-S04 PRES-E07] non-admin navigation cannot grant API privileges or unlock CV AI",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/en/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/en/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/nb/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/nb/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J05 PRES-J07] light loaded status semantics, navigation and command focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-C06 PRES-C07 PRES-C08 PRES-C09 PRES-C11 PRES-S03] light loaded CV tabs, selects and settings",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/en/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/en/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/nb/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/nb/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J05 PRES-J07] dark loaded status semantics, navigation and command focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-C06 PRES-C07 PRES-C08 PRES-C09 PRES-C11 PRES-S03] dark loaded CV tabs, selects and settings",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] light/en Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] light/nb Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] dark/en Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] dark/nb Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] light-en mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] light-en command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] light-en utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] light-en real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] light-en grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E09 C06 S02 S03] light-en dirty editor survives shell utilities and cancel",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] light-nb mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] light-nb command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] light-nb utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] light-nb real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] light-nb grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] dark-en mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] dark-en command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] dark-en utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] dark-en real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] dark-en grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] dark-nb mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] dark-nb command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] dark-nb utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] dark-nb real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] dark-nb grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E09 C06 S02 S03] dark-nb dirty editor survives shell utilities and cancel",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "API liveness and database readiness are separate public probes",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "public plans stay honest, responsive and keyboard accessible",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "login establishes an authenticated session",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "a Free account keeps manual work available while AI actions stay honestly locked",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "a saved job can be created through the reviewed UI flow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "the dedicated application workspace survives deep links, long data and unsaved navigation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "Career Workspace loads from the authenticated application shell",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "a public CV renders anonymously and downloads as PDF",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "the Code template exports a long structured CV as searchable multi-page PDF",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] light/en table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] light/nb table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] dark/en table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] dark/nb table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

@@ -0,0 +1,858 @@
{
"sourceBase": "42a6e425b6e4765c2b41c651b3c7ec8a3235d9f2",
"statistics": {
"startTime": "2026-10-06T17:55:13.491Z",
"duration": 658354.3099999999,
"expected": 83,
"skipped": 0,
"unexpected": 1,
"flaky": 0
},
"actualPasses": 82,
"expectedFailures": 1,
"unexpected": [
"[PRES-S01 S02 E07 E08 E09] Dashboard native data, visibility and responsive presentation"
],
"cases": [
{
"file": "accessibility.spec.ts",
"title": "public entry points meet automated WCAG A and AA checks",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "accessibility.spec.ts",
"title": "primary authenticated workspaces meet automated WCAG A and AA checks",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "retained profile request from A cannot write using B cookies and fresh B CSRF",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "missing and forged session contexts fail closed while same-account profile saves work",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "crafted CV identifiers cannot cross read update delete or job-association ownership",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "[P5-00 PRES-E05 E07 E08] account switch disposes open command, account and notification surfaces",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "account-isolation.spec.ts",
"title": "two tabs discard A's private form before B can use it, including Back navigation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "analytics-presentation.spec.ts",
"title": "[PRES-S01 S02 E07 E08 E09 J05 J09] Analytics native reports, direct entry, period, drill-down and owner isolation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "analytics-presentation.spec.ts",
"title": "[PRES-S01 E08] Analytics independent loading and failure states never masquerade as empty reports",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "board-presentation.spec.ts",
"title": "[PRES-J07 J05 W01 W02 E07 E08 E09] Board query membership matches Table and preserves moves, focus and route",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "board-presentation.spec.ts",
"title": "[PRES-J05 J07 E07 W01] legacy Board replaces history and preserves query, hash and route state; default Jobs stays Table",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "board-presentation.spec.ts",
"title": "[PRES-J07 E08] Board loading, retryable error and fully empty columns",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] light/en preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] dark/nb preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] light/nb preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "builder-preview-keyboard.spec.ts",
"title": "[PRES-C06 PRES-C07 PRES-C10 PRES-E08] dark/en preview keyboard, pagination and responsive state",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "csv-export.spec.ts",
"title": "CSV export has safe cells and headers, and browser download preserves its UTF-8 bytes",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "dashboard-presentation.spec.ts",
"title": "[PRES-S01 S02 E07 E08 E09] Dashboard native data, visibility and responsive presentation",
"expectedStatus": "passed",
"outcome": "unexpected",
"actualStatuses": [
"failed"
],
"attempts": 1
},
{
"file": "dashboard-presentation.spec.ts",
"title": "[PRES-S01 E08] Dashboard loading and independent failed resources retain retry and exact values",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "job-discovery.spec.ts",
"title": "discovery remains usable across widths and opens the reviewed import",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "job-overview-presentation.spec.ts",
"title": "[PRES-W01 W02 W03 J05 E08 E09] native Job header/Overview retains actions, custom status, aliases and mobile progress keyboard access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "job-overview-presentation.spec.ts",
"title": "[PRES-W01 W02 E08] Job Overview loading, failed read, retry and unavailable resource remain explicit",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "kanban-theme.spec.ts",
"title": "kanban columns use dark theme surfaces",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] light/en/1440 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] dark/nb/1440 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] light/nb/390 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "named-controls-modal-state.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J08 PRES-C08 PRES-C09 PRES-W06 PRES-W08] dark/en/390 named controls, modal keyboard and retained draft",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-C01 PRES-C06 PRES-C07 PRES-C10] mobile CV override persists without changing the canonical profile or sibling CV",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-W01 PRES-W05 PRES-W06 PRES-W07 PRES-W08] legacy workspace preserves frozen documents after current sources change",
"expectedStatus": "failed",
"outcome": "expected",
"actualStatuses": [
"failed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-S03 PRES-W11] responsive settings navigation keeps rule edits and persists notification preferences",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-S03] rule changes persist through the real API",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-E07 PRES-W01 PRES-J04] keyboard command navigation and legacy workspace entry preserve the selected job",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "redesign-preservation.spec.ts",
"title": "[PRES-A01 PRES-S04 PRES-E07] non-admin navigation cannot grant API privileges or unlock CV AI",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/en/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/en/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/nb/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] light/nb/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J05 PRES-J07] light loaded status semantics, navigation and command focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-C06 PRES-C07 PRES-C08 PRES-C09 PRES-C11 PRES-S03] light loaded CV tabs, selects and settings",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/en/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/en/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/nb/1440 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-J08] dark/nb/390 loading → error → retry → empty → populated",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E07 PRES-E08 PRES-J05 PRES-J07] dark loaded status semantics, navigation and command focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "semantic-foundations.spec.ts",
"title": "[PRES-E08 PRES-C06 PRES-C07 PRES-C08 PRES-C09 PRES-C11 PRES-S03] dark loaded CV tabs, selects and settings",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] light/en Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] light/nb Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] dark/en Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "settings-page-layout.spec.ts",
"title": "[PRES-E07 E08 E09 S03 S04 S05 J05 J10 M06 W11] dark/nb Settings headings, reflow and retained actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] light-en mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] light-en command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] light-en utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] light-en real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] light-en grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E09 C06 S02 S03] light-en dirty editor survives shell utilities and cancel",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] light-nb mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] light-nb command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] light-nb utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] light-nb real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] light-nb grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] dark-en mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] dark-en command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] dark-en utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] dark-en real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] dark-en grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E08 A03] dark-nb mobile labels, focus and admin reflow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 J03 J08] dark-nb command list, keyboard and no-write actions",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-02 PRES-E07 E08 E09 S02] dark-nb utility search and notification states",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E08 C06 S05] dark-nb real Tooltip contrast and open-toast action access",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-01 PRES-E07 E08 A03] dark-nb grouped shell, rail menu and route focus",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "shell-readiness.spec.ts",
"title": "[P5-00 PRES-E07 E09 C06 S02 S03] dark-nb dirty editor survives shell utilities and cancel",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "API liveness and database readiness are separate public probes",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "public plans stay honest, responsive and keyboard accessible",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "login establishes an authenticated session",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "a Free account keeps manual work available while AI actions stay honestly locked",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "a saved job can be created through the reviewed UI flow",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "the dedicated application workspace survives deep links, long data and unsaved navigation",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "Career Workspace loads from the authenticated application shell",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "a public CV renders anonymously and downloads as PDF",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "smoke.spec.ts",
"title": "the Code template exports a long structured CV as searchable multi-page PDF",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] light/en table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] light/nb table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] dark/en table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
},
{
"file": "table-density.spec.ts",
"title": "[PRES-J05 J06 W01 W02 S03 E08 E09] dark/nb table and cards preserve behavior at both densities",
"expectedStatus": "passed",
"outcome": "expected",
"actualStatuses": [
"passed"
],
"attempts": 1
}
]
}
@@ -0,0 +1,392 @@
{
"sourceBase": "42a6e425b6e4765c2b41c651b3c7ec8a3235d9f2",
"captureRuntime": "Chromium, production export, disposable SQLite API; actual clock 2026-10-06; explicit fixture dates are synthetic",
"manualScreenReaderAcceptance": "open",
"screenshots": [
{
"file": "job-custom-status-dark-nb.png",
"sha256": "d1855565889382767d0f0b5100429c607501435641cb86be23f55ce8c6e01660",
"capturedUtc": "2026-10-06T20:47:23.365387+00:00",
"viewport": {
"width": 1920,
"height": 900
},
"fullPageWidth": 1920,
"fullPageHeight": 1547,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-load-error.png",
"sha256": "8a1677837a324425a4fc11cf35719bc179f5634cbcb3031f383d853ffc7ace03",
"capturedUtc": "2026-10-06T20:47:26.079485+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 844,
"fixture": "controlled local failed transport",
"theme": "light",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-loading.png",
"sha256": "acae5de50304114c906d18a8691a8340dd481aedb0dab0d71245732fb9d52252",
"capturedUtc": "2026-10-06T20:47:26.031990+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 919,
"fixture": "controlled local failed transport",
"theme": "light",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-1920-dark-nb.png",
"sha256": "1cdecd92a4ed2da738fa8aa7048344451af54622020d8dc3dffe8e820b41923e",
"capturedUtc": "2026-10-06T20:47:22.276505+00:00",
"viewport": {
"width": 1920,
"height": 900
},
"fullPageWidth": 1920,
"fullPageHeight": 1516,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-320-dark-nb.png",
"sha256": "d7336e3e0df2d2163e159db3a172eaf4cad06b485a0ad253e5dc686c0a3fc6c1",
"capturedUtc": "2026-10-06T20:47:22.096962+00:00",
"viewport": {
"width": 320,
"height": 900
},
"fullPageWidth": 320,
"fullPageHeight": 2290,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-desktop-dark-en.png",
"sha256": "75abfa4d1bdbcadc85d8e17e5c50d126c6cdad530c6f89874ce77278db651147",
"capturedUtc": "2026-10-06T20:47:17.470778+00:00",
"viewport": {
"width": 1440,
"height": 900
},
"fullPageWidth": 1440,
"fullPageHeight": 1540,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-desktop-dark-nb.png",
"sha256": "814b04894ae843510f63ec32408afb63af2fbf698a7f0ba7fa0bde978c43a61a",
"capturedUtc": "2026-10-06T20:47:21.510893+00:00",
"viewport": {
"width": 1440,
"height": 900
},
"fullPageWidth": 1440,
"fullPageHeight": 1552,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-desktop-light-en.png",
"sha256": "0b0efe8ec0e0571ac95caaf2de2e8d94245ec88a7cfebffc21f1a47a1cc76e92",
"capturedUtc": "2026-10-06T20:47:15.036974+00:00",
"viewport": {
"width": 1440,
"height": 900
},
"fullPageWidth": 1440,
"fullPageHeight": 1540,
"fixture": "native synthetic owned application",
"theme": "light",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-desktop-light-nb.png",
"sha256": "45b4dfd5cfc69c448bb16b4201d212e708121b9035d3e0d3fdbb6520ec99810b",
"capturedUtc": "2026-10-06T20:47:19.794091+00:00",
"viewport": {
"width": 1440,
"height": 900
},
"fullPageWidth": 1440,
"fullPageHeight": 1552,
"fixture": "native synthetic owned application",
"theme": "light",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-phone-dark-en.png",
"sha256": "75d9503ae67d5d5b882a0fb0b036895f16edd9fe1d0ae0445a724d16b5748b15",
"capturedUtc": "2026-10-06T20:47:17.714814+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 2241,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-phone-dark-nb.png",
"sha256": "3a65a0fba13be38d94de182de78cb430cd0cf4547ae9a1928f5a7a783693b7ff",
"capturedUtc": "2026-10-06T20:47:21.763703+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 2223,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-phone-light-en.png",
"sha256": "50c57e1979717040426bb5fa470e80f893ab5fec1c7879763b6c8ef31623fc27",
"capturedUtc": "2026-10-06T20:47:15.331939+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 2241,
"fixture": "native synthetic owned application",
"theme": "light",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-phone-light-nb.png",
"sha256": "20bbaf88d880bd595a4a375556f4af31d1b12e5027f0d40131ddb16e77f1dbde",
"capturedUtc": "2026-10-06T20:47:20.048187+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 2223,
"fixture": "native synthetic owned application",
"theme": "light",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-tablet-dark-en.png",
"sha256": "4de7b3770bb814eb48b2c1a03f0cae8b6beb23e24f5d48eddee4ebcf716df932",
"capturedUtc": "2026-10-06T20:47:17.592514+00:00",
"viewport": {
"width": 768,
"height": 1024
},
"fullPageWidth": 768,
"fullPageHeight": 1809,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-tablet-dark-nb.png",
"sha256": "2e200fdc709a7c888049e6d8de4596b7d36602ee387c0d0d24a166884387831a",
"capturedUtc": "2026-10-06T20:47:21.637567+00:00",
"viewport": {
"width": 768,
"height": 1024
},
"fullPageWidth": 768,
"fullPageHeight": 1818,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-tablet-light-en.png",
"sha256": "321d18bb666f3d7db9b9fc4420346709990d17e07eaeaa35548d3451670566d9",
"capturedUtc": "2026-10-06T20:47:15.192544+00:00",
"viewport": {
"width": 768,
"height": 1024
},
"fullPageWidth": 768,
"fullPageHeight": 1809,
"fixture": "native synthetic owned application",
"theme": "light",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-overview-tablet-light-nb.png",
"sha256": "ec10bbd7f443832651900faa6571a49007df1ab1b2eeb18e0344d34543654b5c",
"capturedUtc": "2026-10-06T20:47:19.922496+00:00",
"viewport": {
"width": 768,
"height": 1024
},
"fullPageWidth": 768,
"fullPageHeight": 1818,
"fixture": "native synthetic owned application",
"theme": "light",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-progress-focus-dark-en.png",
"sha256": "1605563e89584f5fa85401fe2adc14ce06f025e06d25a37a00fc738b78164e68",
"capturedUtc": "2026-10-06T20:47:17.966399+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 2241,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-progress-focus-dark-nb.png",
"sha256": "447aed97cf92a2ef56f27eca5e73429b0a5fb132c623d15f236eb84e22621872",
"capturedUtc": "2026-10-06T20:47:22.013848+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 2223,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-progress-focus-light-en.png",
"sha256": "5c7b6ea105d5bbbe367b972951f9f9e3db802b08405c33c80d119a70dad5ab1b",
"capturedUtc": "2026-10-06T20:47:15.583332+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 2241,
"fixture": "native synthetic owned application",
"theme": "light",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-progress-focus-light-nb.png",
"sha256": "995f631e403af076a88b704954e44c0c79f365578435edde987ca5102f5aa7ea",
"capturedUtc": "2026-10-06T20:47:20.297753+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 2223,
"fixture": "native synthetic owned application",
"theme": "light",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-sparse-dark-nb.png",
"sha256": "acdc0a42e4b554df028edbcc35eef7ac972b6273bb9768b2a35b6f58d6bcd144",
"capturedUtc": "2026-10-06T20:47:24.465309+00:00",
"viewport": {
"width": 1920,
"height": 900
},
"fullPageWidth": 1920,
"fullPageHeight": 1516,
"fixture": "native synthetic owned application",
"theme": "dark",
"language": "nb",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-unavailable.png",
"sha256": "94264f13782ccd78d67bcc248122a3be8e384e3714d4b949efbb582b28f0696c",
"capturedUtc": "2026-10-06T20:47:26.194053+00:00",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 390,
"fullPageHeight": 844,
"fixture": "controlled local failed transport",
"theme": "light",
"language": "en",
"renderedReview": true,
"captureType": "fullPage"
},
{
"file": "job-manual-progress-dark-en.png",
"sha256": "0c42d163eab2f512e2ca8cebf296dc6a33daa7f2358d4a468d00dc16a52c5c65",
"viewport": {
"width": 390,
"height": 844
},
"fullPageWidth": 65536,
"fullPageHeight": 4292542531,
"fixture": "direct computer-use keyboard review of existing disposable CSV job; no edit saved",
"theme": "dark",
"language": "en",
"renderedReview": true,
"note": "viewport image width 375 after 15px scrollbar; focus +40px native scroll; not a manual screen-reader check",
"captureType": "viewport",
"capturedUtc": "2026-10-06T20:42:39.389131+00:00"
}
]
}
@@ -0,0 +1,287 @@
{
"manualScreenReaderAcceptance": "open",
"scans": [
{
"name": "job-overview-light-axe",
"violations": [],
"incomplete": [
{
"id": "color-contrast",
"impact": "serious",
"description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
"nodes": [
{
"target": [
".MuiBadge-invisible"
],
"checks": [
{
"id": "color-contrast",
"message": "Element content is too short to determine if it is actual text content"
}
]
},
{
"target": [
".css-1vcc3k.MuiBox-root:nth-child(4) > .css-b7tg9p.MuiStack-root > .css-1etkf64.MuiTypography-caption.MuiTypography-root"
],
"checks": [
{
"id": "color-contrast",
"message": "Element's background color could not be determined because it's partially obscured by another element"
}
]
},
{
"target": [
".css-1vcc3k.MuiBox-root:nth-child(5) > .css-b7tg9p.MuiStack-root > .css-1etkf64.MuiTypography-caption.MuiTypography-root"
],
"checks": [
{
"id": "color-contrast",
"message": "Element's background color could not be determined because it's partially obscured by another element"
}
]
},
{
"target": [
".css-1h36fbo"
],
"checks": [
{
"id": "color-contrast",
"message": "Element's background color could not be determined because it's partially obscured by another element"
}
]
},
{
"target": [
".css-1vcc3k.MuiBox-root:nth-child(7) > .css-b7tg9p.MuiStack-root > .css-1etkf64.MuiTypography-caption.MuiTypography-root"
],
"checks": [
{
"id": "color-contrast",
"message": "Element's background color could not be determined because it's partially obscured by another element"
}
]
}
]
},
{
"id": "duplicate-id-aria",
"impact": "critical",
"description": "Ensure every id attribute value used in ARIA and in labels is unique",
"nodes": [
{
"target": [
"div[aria-labelledby=\"details-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: details-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"tasks-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: tasks-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"calendar-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: calendar-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"timeline-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: timeline-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"documents-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: documents-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"communication-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: communication-content"
}
]
}
]
}
]
},
{
"name": "job-overview-dark-axe",
"violations": [],
"incomplete": [
{
"id": "color-contrast",
"impact": "serious",
"description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
"nodes": [
{
"target": [
".MuiBadge-invisible"
],
"checks": [
{
"id": "color-contrast",
"message": "Element content is too short to determine if it is actual text content"
}
]
},
{
"target": [
".css-1vcc3k.MuiBox-root:nth-child(4) > .css-b7tg9p.MuiStack-root > .css-1etkf64.MuiTypography-caption.MuiTypography-root"
],
"checks": [
{
"id": "color-contrast",
"message": "Element's background color could not be determined because it's partially obscured by another element"
}
]
},
{
"target": [
".css-1vcc3k.MuiBox-root:nth-child(5) > .css-b7tg9p.MuiStack-root > .css-1etkf64.MuiTypography-caption.MuiTypography-root"
],
"checks": [
{
"id": "color-contrast",
"message": "Element's background color could not be determined because it's partially obscured by another element"
}
]
},
{
"target": [
".css-1h36fbo"
],
"checks": [
{
"id": "color-contrast",
"message": "Element's background color could not be determined because it's partially obscured by another element"
}
]
},
{
"target": [
".css-1vcc3k.MuiBox-root:nth-child(7) > .css-b7tg9p.MuiStack-root > .css-1etkf64.MuiTypography-caption.MuiTypography-root"
],
"checks": [
{
"id": "color-contrast",
"message": "Element's background color could not be determined because it's partially obscured by another element"
}
]
}
]
},
{
"id": "duplicate-id-aria",
"impact": "critical",
"description": "Ensure every id attribute value used in ARIA and in labels is unique",
"nodes": [
{
"target": [
"div[aria-labelledby=\"details-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: details-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"tasks-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: tasks-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"calendar-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: calendar-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"timeline-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: timeline-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"documents-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: documents-content"
}
]
},
{
"target": [
"div[aria-labelledby=\"communication-header\"]"
],
"checks": [
{
"id": "duplicate-id-aria",
"message": "Document has multiple elements referenced with ARIA with the same id attribute: communication-content"
}
]
}
]
}
]
}
]
}
+5 -2
View File
@@ -4,7 +4,9 @@ Status reconciled: 2026-10-06.
## Current repository work
- **P5-07 — existing-report Analytics destination complete:** meaningful authenticated `/analytics`/Insights entry, shared retained Dashboard report owner, current-versus-window definitions, exact values and existing status/company drill-downs. [Record](../redesign/phase-5/p5-07/README.md). Baseline: 934 backend, 403 frontend (+5), 81 actual browser passes (+2) plus unchanged P2-D01 expected failure. Dashboard's five owner-local flags and all original routes remain; no new metrics/backend contracts. P5-08a and B07 are not started; F04, P2-D01, incomplete axe/manual screen-reader, P5-06-O01 and the P5-07-O01 observation remain open.
- **P5-08a — Job header and Overview complete:** shared role/company/status/actions header, recorded date summary, native preparation entries and keyboard-scrollable progress; five section keys/six deeper disclosures and all route/domain owners retained. [Record](../redesign/phase-5/p5-08a/README.md). Baseline: 934 backend, 408 frontend (+5), 83 actual browser passes (+2) plus unchanged P2-D01 expected failure; security 9/6/1, configuration 3. P4-02-D03 closes locally. P5-08b is **NO-GO pending native package proof and its own adapter/draft/AI/deeper-accessibility gates**; no B07 or deeper migration. F04 and manual screen-reader acceptance remain open.
- **P5-07 — existing-report Analytics destination complete:** meaningful authenticated `/analytics`/Insights entry, shared retained Dashboard report owner, current-versus-window definitions, exact values and existing status/company drill-downs. [Record](../redesign/phase-5/p5-07/README.md). Baseline: 934 backend, 403 frontend (+5), 81 actual browser passes (+2) plus unchanged P2-D01 expected failure. Dashboard's five owner-local flags and all original routes remain; no new metrics/backend contracts. P5-08a is recorded separately above; F04, P2-D01, incomplete axe/manual screen-reader, P5-06-O01 and the P5-07-O01 observation remain open.
- **P5-06 — personal Dashboard presentation complete:** existing reports, actions and five owner-local visibility flags retained; precise population/period/sample captions, shared-scale activity and exact keyboard-accessible values, independent error/retry, and native account-switch evidence. [Record](../redesign/phase-5/p5-06/README.md). Baseline: 934 backend, 398 frontend (+8), 79 actual browser passes (+2) plus unchanged P2-D01 expected failure; security 9/6/1, configuration 3. No new metrics/backend contracts or Phase 8 customization. P5-07 is recorded separately above; F04, incomplete axe and manual screen-reader acceptance remain open.
- **P5-06-O01 — native language-preference 500, cause unproven:** the first full browser attempt observed a generic 500 during overlapping Dashboard/setup reads after Browser Back. Visual preparation now requires the native preference save before mounting the next Dashboard; no retry or backend repair. Investigate the unchanged endpoint separately; concurrent language writes during loading remain uncertified. [Evidence](../redesign/phase-5/p5-06/README.md#regression-protection).
@@ -22,7 +24,8 @@ Status reconciled: 2026-10-06.
- **Phase 5 plan:** [bounded plan](../redesign/phase-5/README.md) maps all 57 contracts/31 routes. Board integration, native package and CV revision/rendering gates apply only to dependent slices. F04 remains open.
- **P2-D01 — open:** repair the SQLite `DateTimeOffset` ordering baseline before package/tailoring restructuring; retain the expected-failure browser oracle until native capture and historical-binary preservation are proven. P2-O01's initial rules-500 behavior also remains unconfirmed.
- **P3-O01/P3-O02 — scoped follow-up:** P5-06/P5-07 address Dashboard/Analytics population/period/sample presentation and independent error retention. Backend metric definitions and other navigation/query surfaces remain separately scoped. Evidence is in the current [redesign decision records](../redesign/phase-3/OPEN_DECISIONS.md).
- **P4-02-D02–D04 — open accessibility/content debt:** company save-error feedback outside the modal's accessible subtree, mobile application-progress scroll region and literal AI context `{name}`. D01 command-list semantics are addressed in P5-00; D02–D04 are unchanged. [Original evidence](../redesign/phase-4/p4-02/README.md). Do not disable modal background isolation or hide findings.
- **P4-02-D02/D04 — open accessibility/content debt:** company save-error feedback outside the modal's accessible subtree and literal AI context `{name}`. D01 command-list semantics are addressed in P5-00; D03 progress-scroll access closes for the targeted P5-08a strip. D02/D04 remain unchanged. [Original evidence](../redesign/phase-4/p4-02/README.md). Do not disable modal background isolation or hide findings.
- **P5-08a-O01/O03 — deeper workspace entry/accessibility debt:** unsupported next-task aliases and duplicated content IDs in unchanged accordions require P5-08b's own adapter/composition evidence. **O02** retains two failed menu-contrast runs and the explicitly justified settled-state fixture wait; no Dashboard styling change or broader manual certification. [Record](../redesign/phase-5/p5-08a/README.md).
- **P5-00 scoped closures:** P4-03-D01 Tooltip contrast, P4-04-O01 bottom-action toast overlap, P4-06-O01 mobile labels/focus and P4-06-O02 synthetic long admin metadata are addressed with [readiness evidence](../redesign/phase-5/p5-00/README.md). This does not certify all overlays, all accessibility targets or deployed production behavior.
- **P4-04-O02 — existing SQLite notification-save contention:** overlapping native preference read/write requests reproduced HTTP 500 with SQLite error 5 in two of ten local runs. The sequential UI test now awaits and requires the save response before readback; server concurrency remains unresolved. Investigate separately from page-layout/density work. [Evidence](../redesign/phase-4/p4-04/README.md).
- **P4-05-O01 — closed for Jobs/Trash:** P5-03 localizes standard status display and date labels while retaining raw API status values, custom labels and stored dates. [Evidence](../redesign/phase-5/p5-03/README.md).
@@ -91,6 +91,8 @@ test('[PRES-S01 S02 E07 E08 E09] Dashboard native data, visibility and responsiv
await customize.focus(); await page.keyboard.press('Enter');
await expect(page.getByRole('menuitemcheckbox').first()).toBeFocused();
if (language === 'en') {
// Measure settled colors, not the menu's entry-opacity transition.
await expect.poll(() => page.getByRole('menu').evaluate(menu => getComputedStyle(menu.parentElement!).opacity)).toBe('1');
const menuScan = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']).analyze();
await info.attach(`dashboard-${mode}-menu-axe`, { body: JSON.stringify({ violations: menuScan.violations, incomplete: menuScan.incomplete }), contentType: 'application/json' });
expect(menuScan.violations).toEqual([]);
@@ -0,0 +1,127 @@
import { expect, test, type APIRequestContext, type Page, type TestInfo } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
import { translations } from '../src/i18n/translations';
const apiUrl = 'http://localhost:5302/api';
const password = 'E2ePassword123!';
async function login(api: APIRequestContext, email: string) {
expect((await api.post(`${apiUrl}/auth/login`, { data: { email, password } })).status()).toBe(200);
const me = await (await api.get(`${apiUrl}/auth/me`)).json();
const csrf = (await api.storageState()).cookies.find(cookie => cookie.name === 'XSRF-TOKEN')!.value;
return { me, headers: { 'X-CSRF-TOKEN': csrf, 'X-Session-Context': me.sessionContext as string } };
}
async function capture(page: Page, info: TestInfo, name: string) {
const path = info.outputPath(`${name}.png`);
await page.screenshot({ path, fullPage: true });
await info.attach(name, { path, contentType: 'image/png' });
}
test('[PRES-W01 W02 W03 J05 E08 E09] native Job header/Overview retains actions, custom status, aliases and mobile progress keyboard access', async ({ page, playwright }, info) => {
test.setTimeout(240000);
const admin = await playwright.request.newContext();
const { headers: adminHeaders } = await login(admin, 'e2e@example.test');
const email = `job-overview-${Date.now()}@example.test`;
expect((await admin.post(`${apiUrl}/users`, { headers: adminHeaders, data: { email, password, roles: [] } })).status()).toBe(200);
const { headers } = await login(page.request, email);
await page.route('**/*', route => ['localhost', '127.0.0.1'].includes(new URL(route.request().url()).hostname) ? route.continue() : route.abort());
const companyResponse = await page.request.post(`${apiUrl}/companies`, { headers, data: { name: 'Nordlys karrieremuligheter og langsiktig plattformutvikling æøå', source: 'direct' } });
expect(companyResponse.status()).toBe(201);
const companyId = (await companyResponse.json()).id;
const title = 'Senior utvikler og faglig rådgiver for samfunnskritiske plattformer og inkluderende digitale tjenester';
const jobData = { companyId, jobTitle: title, status: 'Interview', location: 'Tromsø · Oslo og fjernarbeid', salary: '700 000–900 000 NOK', deadline: '2026-12-14T12:00:00Z', dateApplied: '2026-10-02T12:00:00Z', followUpAt: '2026-10-12T12:00:00Z', jobUrl: 'https://example.test/jobs/platform', notes: 'Ask about ownership and sustainable delivery.', description: 'Build reliable services with .NET, SQL and Azure.', tags: 'Azure, .NET', source: 'direct', countryCode: 'NO' };
const created = await page.request.post(`${apiUrl}/jobapplications`, { headers, data: jobData });
expect(created.status()).toBe(201); const id = (await created.json()).id;
const sparseResponse = await page.request.post(`${apiUrl}/jobapplications`, { headers, data: { companyId, jobTitle: 'Sparse application', status: 'Saved' } });
expect(sparseResponse.status()).toBe(201); const sparseId = (await sparseResponse.json()).id;
const writes: string[] = [];
page.on('request', request => {
if (request.url().startsWith(apiUrl) && !['GET', 'OPTIONS'].includes(request.method())) writes.push(`${request.method()} ${new URL(request.url()).pathname}`);
});
await page.goto(`/jobs/${id}`);
await expect(page.getByRole('heading', { name: title, level: 2 })).toBeVisible();
await expect(page.getByRole('tablist', { name: translations.en.workspaceSections }).getByRole('tab')).toHaveCount(5);
await expect(page.getByRole('link', { name: translations.en.workspaceOpenAdvert })).toHaveAttribute('href', jobData.jobUrl);
await expect(page.getByRole('link', { name: translations.en.workspaceOpenAdvert })).toHaveAttribute('rel', 'noopener noreferrer');
expect(writes).toEqual([]);
const edit = page.getByRole('button', { name: translations.en.workspaceEditApplication });
await edit.focus(); await page.keyboard.press('Enter');
await expect(page.getByRole('dialog')).toBeVisible();
await page.keyboard.press('Escape'); await expect(edit).toBeFocused();
for (const language of ['en', 'nb'] as const) for (const mode of ['light', 'dark'] as const) {
const t = translations[language];
await page.evaluate(({ mode, language }) => { localStorage.setItem('jobtracker.themeMode', mode); localStorage.setItem('uiLanguage', language); }, { mode, language });
// Persist language while no app reads are in flight; theme uses its existing local owner.
await page.goto('about:blank');
expect((await page.request.put(`${apiUrl}/auth/preferences/language`, { headers, data: { language: language === 'nb' ? 'nb-NO' : 'en' } })).status()).toBe(204);
await page.goto(`/jobs/${id}?section=overview`);
await expect(page.getByRole('heading', { name: title, level: 2 })).toBeVisible();
await expect(page.locator('html')).toHaveAttribute('data-color-scheme', mode);
const summary = page.getByRole('region', { name: t.workspaceApplicationInfo, exact: true });
await expect(summary).toContainText(new Date(jobData.dateApplied).toLocaleDateString(language === 'nb' ? 'nb-NO' : 'en'));
const progress = page.getByRole('region', { name: `${t.workspaceProgress}: ${t.statusInterview}` });
await expect(progress.locator('[aria-current=step]')).toHaveText(t.statusInterview);
for (const [width, height, name] of [[1440, 900, 'desktop'], [768, 1024, 'tablet'], [390, 844, 'phone']] as const) {
await page.setViewportSize({ width, height });
await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1)).toBe(true);
await capture(page, info, `job-overview-${name}-${mode}-${language}`);
}
await progress.focus();
expect(await progress.evaluate(element => parseFloat(getComputedStyle(element).outlineWidth))).toBeGreaterThanOrEqual(3);
await progress.evaluate(element => { element.scrollLeft = 0; });
await page.keyboard.press('ArrowRight');
await expect.poll(() => progress.evaluate(element => element.scrollLeft)).toBeGreaterThan(0);
await capture(page, info, `job-progress-focus-${mode}-${language}`);
if (language === 'en') {
const scan = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']).analyze();
await info.attach(`job-overview-${mode}-axe`, { body: JSON.stringify({ violations: scan.violations, incomplete: scan.incomplete }), contentType: 'application/json' });
expect(scan.violations).toEqual([]);
}
}
for (const width of [320, 1920]) {
await page.setViewportSize({ width, height: 900 });
await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1)).toBe(true);
await capture(page, info, `job-overview-${width}-dark-nb`);
}
// Existing native status write, not a new header status API or inferred progression.
await page.goto('about:blank');
expect((await page.request.put(`${apiUrl}/jobapplications/${id}`, { headers, data: { ...jobData, status: 'Faglig vurdering' } })).status()).toBe(204);
await page.goto(`/applications/${id}?section=overview`);
const custom = page.getByRole('region', { name: `${translations.nb.workspaceProgress}: Faglig vurdering` });
await expect(custom).toContainText(translations.nb.workspaceCustomProgress);
await expect(custom.locator('[aria-current]')).toHaveCount(0);
await capture(page, info, 'job-custom-status-dark-nb');
await page.goto(`/jobs/${sparseId}`);
await expect(page.getByRole('heading', { name: 'Sparse application' })).toBeVisible();
await expect(page.getByRole('region', { name: translations.nb.workspaceApplicationInfo }).getByText('—', { exact: true })).toHaveCount(3);
await expect(page.getByRole('link', { name: translations.nb.workspaceOpenAdvert })).toHaveCount(0);
await capture(page, info, 'job-sparse-dark-nb');
const foreignEmail = `job-overview-foreign-${Date.now()}@example.test`;
expect((await admin.post(`${apiUrl}/users`, { headers: adminHeaders, data: { email: foreignEmail, password, roles: [] } })).status()).toBe(200);
const foreign = await playwright.request.newContext();
await login(foreign, foreignEmail);
expect((await foreign.get(`${apiUrl}/jobapplications/${id}/workspace`)).status()).toBe(404);
await foreign.dispose(); await admin.dispose();
});
test('[PRES-W01 W02 E08] Job Overview loading, failed read, retry and unavailable resource remain explicit', async ({ page }, info) => {
await login(page.request, 'e2e@example.test');
let release: (() => void) | undefined; let failed = true;
const hold = new Promise<void>(resolve => { release = resolve; });
await page.route(`${apiUrl}/jobapplications/2147483646/workspace`, async route => {
await hold;
return route.fulfill({ status: failed ? 503 : 404, json: { message: failed ? 'Temporary workspace failure' : 'Not Found' } });
});
await page.setViewportSize({ width: 390, height: 844 });
await page.goto('/jobs/2147483646');
await expect(page.getByRole('progressbar').first()).toBeVisible();
await capture(page, info, 'job-loading'); release!();
await expect(page.getByRole('alert').filter({ hasText: 'Could not open this application.' })).toBeVisible();
await capture(page, info, 'job-load-error'); failed = false;
await page.getByRole('button', { name: 'Retry', exact: true }).click();
await expect(page.getByRole('alert').filter({ hasText: 'Not Found' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Retry', exact: true })).toHaveCount(0);
await expect(page.getByRole('button', { name: 'Back to applications' })).toBeVisible();
await capture(page, info, 'job-unavailable');
});
@@ -274,3 +274,48 @@ test("hydrates list filters, sort and page from a shareable URL", async () => {
}),
}));
});
test("names the progress scroll region and identifies the current stage without making stages actions", async () => {
renderTable("/jobs/42");
const progress = await screen.findByRole("region", { name: "Application progress: Waiting" });
expect(progress).toHaveAttribute("tabindex", "0");
expect(within(progress).getByText("Waiting").closest("[aria-current]")).toHaveAttribute("aria-current", "step");
expect(within(progress).queryByRole("button")).not.toBeInTheDocument();
});
test("keeps custom status literal without claiming a completed standard pipeline", async () => {
mockedApi.get.mockResolvedValue({ data: { ...overview, status: "Faglig vurdering" } } as any);
renderTable("/jobs/42");
const progress = await screen.findByRole("region", { name: "Application progress: Faglig vurdering" });
expect(within(progress).queryByText("Offer")).not.toBeInTheDocument();
expect(await screen.findByRole("heading", { name: "Backend Developer" })).toBeInTheDocument();
});
test("retries a failed workspace read without creating or changing application content", async () => {
mockedApi.get.mockRejectedValueOnce({ response: { status: 503 } });
renderTable("/jobs/42");
fireEvent.click(await screen.findByRole("button", { name: "Retry" }));
expect(await screen.findByRole("heading", { name: "Backend Developer" })).toBeInTheDocument();
expect(mockedApi.post).not.toHaveBeenCalled();
expect(mockedApi.put).not.toHaveBeenCalled();
});
test("keeps the server-selected next action and preparation entry on the existing CV section", async () => {
mockedApi.get.mockResolvedValue({ data: { ...overview, nextStep: { key: "prepare-cv", label: "Prepare a CV for this role", reason: "Attach the chosen variant.", section: "cv" } } } as any);
renderTable("/jobs/42");
fireEvent.click(await screen.findByRole("button", { name: "Prepare a CV for this role" }));
expect(await screen.findByText("CV section")).toBeInTheDocument();
expect(screen.getByTestId("location")).toHaveTextContent("/jobs/42?section=cv");
fireEvent.click(screen.getByRole("tab", { name: "Overview" }));
fireEvent.click(await screen.findByRole("button", { name: "CV Not prepared" }));
expect(await screen.findByText("CV section")).toBeInTheDocument();
expect(mockedApi.post).not.toHaveBeenCalled();
});
test("uses recorded dates in the summary and keeps absent dates unknown", async () => {
renderTable("/jobs/42");
const summary = await screen.findByRole("region", { name: "Application information" });
expect(summary).toHaveTextContent(new Date(overview.dateApplied).toLocaleDateString("en"));
expect(within(summary).getAllByText("—")).toHaveLength(2);
expect(screen.queryByRole("link", { name: "Open original advert" })).not.toBeInTheDocument();
});
+4
View File
@@ -2422,6 +2422,8 @@ export const translations = {
workspaceEditApplication: "Edit application",
workspaceOpenAdvert: "Open original advert",
workspaceProgress: "Application progress",
workspaceCustomProgress: "This custom status has no standard pipeline position.",
workspacePreparation: "Application preparation",
workspaceNextAction: "Next recommended action",
workspaceNothingOutstanding: "Nothing outstanding — this application is fully prepared.",
workspaceRecentActivity: "Recent activity",
@@ -4936,6 +4938,8 @@ export const translations = {
workspaceEditApplication: "Rediger søknad",
workspaceOpenAdvert: "Åpne den opprinnelige annonsen",
workspaceProgress: "Søknadsprogresjon",
workspaceCustomProgress: "Denne egendefinerte statusen har ingen plass i standardløpet.",
workspacePreparation: "Forberedelse av søknaden",
workspaceNextAction: "Neste anbefalte handling",
workspaceNothingOutstanding: "Ingenting gjenstår — denne søknaden er ferdig forberedt.",
workspaceRecentActivity: "Nylig aktivitet",
@@ -4,7 +4,7 @@ import {
} from "react-router-dom";
import {
Accordion, AccordionDetails, AccordionSummary, Alert, Box, Button, Chip, Divider, IconButton, Paper,
Accordion, AccordionDetails, AccordionSummary, Alert, Box, Button, Chip, IconButton, Paper,
Skeleton, Stack, Tab, Tabs, Tooltip, Typography,
} from "@mui/material";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
@@ -31,6 +31,9 @@ import {
} from "../components/ApplicationAssets";
import { ApplicationInterviewPrep } from "../components/InterviewPrep";
import EditJobDialog from "../components/EditJobDialog";
import ViewStateNotice from "../components/ViewStateNotice";
import { PageHeader, PageSection } from "../layout/PageLayout";
import type { ViewResourceError } from "../hooks/useViewResource";
import { ApplicationStatusSuggestion, ApplicationStrategySnapshot } from "../components/ApplicationWorkflowAssist";
import { PIPELINE_STATUSES, normalizeStatus, statusLabel, statusTone } from "../pipeline";
import { useI18n } from "../i18n/I18nProvider";
@@ -74,7 +77,8 @@ export function ApplicationWorkspace({
const section = sectionOverride ?? workspaceSection(params.get("section"));
const [overview, setOverview] = useState<WorkspaceOverview | null>(null);
const [error, setError] = useState<string | null>(null);
const [error, setError] = useState<ViewResourceError | null>(null);
const [loading, setLoading] = useState(true);
const [editOpen, setEditOpen] = useState(false);
const [coverLetterDirty, setCoverLetterDirty] = useState(false);
const [packageDraftsDirty, setPackageDraftsDirty] = useState(false);
@@ -115,14 +119,19 @@ export function ApplicationWorkspace({
const load = useCallback(async () => {
if (!Number.isInteger(jobId) || jobId <= 0) {
setOverview(null);
setError(t("workspaceInvalidLink"));
setError({ kind: "error", message: t("workspaceInvalidLink"), retryable: false });
setLoading(false);
return;
}
try {
setError(null);
setLoading(true);
setOverview(await applicationWorkspaceApi.overview(jobId));
} catch (err) {
setError(getApiErrorMessage(err, t("workspaceLoadFailed")));
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);
}
}, [jobId, t]);
@@ -146,16 +155,17 @@ export function ApplicationWorkspace({
if (error) {
return (
<Box sx={{ p: 3 }}>
<Box sx={{ display: "grid", gap: 2, minWidth: 0 }}>
<Button startIcon={<ArrowBackIcon />} onClick={close}>{t("workspaceBack")}</Button>
<Alert severity="error" sx={{ mt: 2 }}>{error}</Alert>
<ViewStateNotice title={t("workspaceLoadFailed")} error={error} onRetry={load} compact />
</Box>
);
}
return (
<Box sx={{ display: "grid", gap: 2 }}>
<Paper sx={{ borderRadius: 3, overflow: "hidden" }}>
<Box sx={{ display: "grid", gap: "var(--app-section-gap)", minWidth: 0 }}>
<ViewStateNotice title={t("workspaceLoadFailed")} loading={loading && !overview} compact />
<Paper variant="outlined" sx={{ minWidth: 0, borderRadius: 1 }}>
<Stack direction="row" alignItems="center" spacing={0.5} sx={{ px: { xs: 1, sm: 2 }, pt: 1.5 }}>
<Tooltip title={t("workspaceBack")}>
<IconButton size="small" aria-label={t("workspaceBack")} onClick={close}>
@@ -229,34 +239,19 @@ export function ApplicationWorkspace({
);
}
function WorkspaceHeader({ overview, onEdit }: { overview: WorkspaceOverview | null; onEdit: () => void }) {
const { t } = useI18n();
if (!overview) return <Paper sx={{ p: 2.5, borderRadius: 3 }}><Skeleton width="45%" height={34} /><Skeleton width="30%" /></Paper>;
const { language, t } = useI18n();
if (!overview) return <Box sx={{ p: 2 }} aria-hidden><Skeleton width="65%" height={34} /><Skeleton width="40%" /></Box>;
return (
<Box sx={{ px: { xs: 2, sm: 3 }, pt: 1, pb: 2.5 }}>
<Stack direction="row" alignItems="flex-start" justifyContent="space-between" flexWrap="wrap" gap={1}>
<Box>
<Typography variant="h5" sx={{ fontWeight: 900 }}>{overview.jobTitle}</Typography>
<Typography color="text.secondary">
{[overview.company, overview.location, overview.salary].filter(Boolean).join(" · ") || "—"}
</Typography>
</Box>
<Stack direction="row" spacing={1} alignItems="center">
<Tooltip title={t("workspaceEditApplication")}>
<IconButton size="small" aria-label={t("workspaceEditApplication")} onClick={onEdit}>
<EditOutlinedIcon fontSize="small" />
</IconButton>
</Tooltip>
<Chip size="small" label={statusLabel(t, overview.status)} color={statusTone(overview.status)} variant="outlined" />
{overview.source ? <Chip size="small" label={overview.source.toUpperCase()} variant="outlined" /> : null}
{overview.jobUrl && (
<Tooltip title={t("workspaceOpenAdvert")}>
<IconButton size="small" aria-label={t("workspaceOpenAdvert")} href={overview.jobUrl} target="_blank" rel="noopener noreferrer">
<OpenInNewIcon fontSize="small" />
</IconButton>
</Tooltip>
)}
</Stack>
</Stack>
<Box sx={{ px: { xs: 2, sm: 3 }, pt: 1, pb: 2 }}>
<PageHeader level="h2" title={overview.jobTitle} description={overview.company ?? undefined} actions={<>
<Button variant="outlined" startIcon={<EditOutlinedIcon />} onClick={onEdit}>{t("workspaceEditApplication")}</Button>
{overview.jobUrl ? <Button component="a" href={overview.jobUrl} target="_blank" rel="noopener noreferrer" endIcon={<OpenInNewIcon />}>{t("workspaceOpenAdvert")}</Button> : null}
</>} />
<Box sx={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: 1, mt: 1.5, minWidth: 0 }}>
<Chip size="small" label={statusLabel(t, overview.status)} color={statusTone(overview.status)} variant="outlined" sx={{ maxWidth: "100%", height: "auto", minHeight: 24, "& .MuiChip-label": { whiteSpace: "normal", overflowWrap: "anywhere" } }} />
{overview.location ? <Typography variant="body2" color="text.secondary" sx={{ overflowWrap: "anywhere" }}>{overview.location}</Typography> : null}
{overview.deadline ? <Typography variant="body2" color="text.secondary">{t("jobDetailsDeadline")}: {new Date(overview.deadline).toLocaleDateString(language === "nb" ? "nb-NO" : "en")}</Typography> : null}
</Box>
<ApplicationProgress status={overview.status} />
</Box>
);
@@ -265,18 +260,19 @@ function WorkspaceHeader({ overview, onEdit }: { overview: WorkspaceOverview | n
function ApplicationProgress({ status }: { status: string }) {
const { t } = useI18n();
const normalized = normalizeStatus(status);
const currentOrder = normalized === "Other" ? Number.MAX_SAFE_INTEGER : PIPELINE_STATUSES.indexOf(normalized) + 1;
const currentOrder = normalized === "Other" ? 0 : PIPELINE_STATUSES.indexOf(normalized) + 1;
const isTerminal = normalized === "Rejected" || normalized === "Ghosted" || normalized === "Withdrawn";
const stages = isTerminal
const stages = normalized === "Other" ? [] : isTerminal
? [...PIPELINE_STATUSES.filter((stage) => !["Offer", "Rejected", "Ghosted", "Withdrawn"].includes(stage)), normalized]
: PIPELINE_STATUSES.filter((stage) => !["Rejected", "Ghosted", "Withdrawn"].includes(stage));
return (
<Box sx={{ mt: 2.5 }} aria-label={`${t("workspaceProgress")}: ${statusLabel(t, status)}`}>
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 800, letterSpacing: ".06em", textTransform: "uppercase" }}>
<Box sx={{ mt: 2 }}>
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 700 }}>
{t("workspaceProgress")}
</Typography>
<Box sx={{ display: "flex", overflowX: "auto", pt: 1, pb: 0.5 }}>
<Box role="region" tabIndex={0} aria-label={`${t("workspaceProgress")}: ${statusLabel(t, status)}`} sx={{ display: "flex", overflowX: "auto", p: 1, mt: 0.5, borderRadius: 1 }}>
{normalized === "Other" ? <Typography variant="body2" color="text.secondary">{t("workspaceCustomProgress")}</Typography> : null}
{stages.map((stage, index) => {
const order = PIPELINE_STATUSES.indexOf(stage) + 1;
const current = stage === normalized;
@@ -284,7 +280,7 @@ function ApplicationProgress({ status }: { status: string }) {
return (
<Box key={stage} sx={{ display: "grid", gridTemplateColumns: index ? "minmax(30px, 1fr) auto" : "auto", alignItems: "center", minWidth: index ? 110 : 72, flex: 1 }}>
{index > 0 && <Box aria-hidden sx={{ height: 2, bgcolor: complete || current ? "primary.main" : "divider" }} />}
<Stack alignItems="center" spacing={0.5} sx={{ minWidth: 72 }}>
<Stack aria-current={current ? "step" : undefined} alignItems="center" spacing={0.5} sx={{ minWidth: 72 }}>
<Box aria-hidden sx={{ width: 12, height: 12, borderRadius: "50%", border: 2, borderColor: current || complete ? "primary.main" : "divider", bgcolor: complete ? "primary.main" : "background.paper", boxShadow: current ? (theme) => `0 0 0 4px ${theme.palette.primary.main}26` : "none" }} />
<Typography variant="caption" sx={{ whiteSpace: "nowrap", fontWeight: current ? 800 : 600, color: current ? "text.primary" : "text.secondary" }}>
{statusLabel(t, stage)}
@@ -326,57 +322,65 @@ function OverviewSection({ overview, onGo, onReload, onEdit }: {
: null;
return (
<Stack spacing={2}>
<Stack spacing={2} sx={{ minWidth: 0 }}>
<PageSection surface title={t("workspaceApplicationInfo")}>
<Box component="dl" sx={{ m: 0, display: "grid", gridTemplateColumns: { xs: "1fr", sm: "repeat(2, minmax(0, 1fr))", lg: "repeat(4, minmax(0, 1fr))" }, gap: 2 }}>
{[
[t("workspaceDiscovered"), overview.savedAt],
[t("workspaceApplied"), overview.dateApplied],
[t("jobDetailsDeadline"), overview.deadline],
[t("intelligenceCategoryFollowUp"), overview.followUpAt],
].map(([label, date]) => <Box key={label}>
<Typography component="dt" variant="caption" color="text.secondary">{label}</Typography>
<Typography component="dd" variant="body2" sx={{ m: 0, mt: 0.5, fontWeight: 600 }}>{date ? new Date(date).toLocaleDateString(language === "nb" ? "nb-NO" : "en") : "—"}</Typography>
</Box>)}
</Box>
</PageSection>
{overview.nextStep ? (
<Paper sx={{ p: 2.5, borderRadius: 3, borderLeft: "4px solid", borderLeftColor: "primary.main" }}>
<Typography variant="overline" color="text.secondary">{t("workspaceNextAction")}</Typography>
<Typography variant="h6" sx={{ fontWeight: 800 }}>{nextStepLabel}</Typography>
<PageSection surface title={t("workspaceNextAction")}>
<Typography component="h3" variant="subtitle1" sx={{ fontWeight: 700 }}>{nextStepLabel}</Typography>
<Typography color="text.secondary" sx={{ mb: 1.5 }}>{nextStepReason}</Typography>
<Button variant="contained" endIcon={<ArrowForwardIcon />}
onClick={() => overview.nextStep?.section && onGo(overview.nextStep.section as WorkspaceSectionKey)}>
{nextStepLabel}
</Button>
</Paper>
</PageSection>
) : (
<Alert severity="success" sx={{ borderRadius: 3 }}>
{t("workspaceNothingOutstanding")}
</Alert>
)}
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", sm: "repeat(2, minmax(0, 1fr))", lg: "repeat(5, minmax(0, 1fr))" }, gap: 1.5 }}>
<PageSection title={t("workspacePreparation")}>
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", sm: "repeat(2, minmax(0, 1fr))", lg: "repeat(5, minmax(0, 1fr))" }, gap: 1 }}>
{stats.map((s) => (
<Paper key={s.label} variant="outlined" role="button" tabIndex={0}
<Button key={s.label} variant="outlined"
onClick={() => onGo(s.go)}
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onGo(s.go); } }}
sx={{ p: 1.5, borderRadius: 3, cursor: "pointer", "&:focus-visible": { boxShadow: 3 } }}>
<Stack direction="row" spacing={0.75} alignItems="center" sx={{ color: s.ok ? "success.main" : "text.disabled" }}>
sx={{ p: 1.5, display: "block", textAlign: "left", color: "text.primary", borderColor: "divider", minWidth: 0, whiteSpace: "normal", overflowWrap: "anywhere" }}>
<Stack direction="row" spacing={0.75} alignItems="center" sx={{ color: s.ok ? "success.main" : "text.secondary" }}>
{s.icon}
<Typography variant="caption" sx={{ fontWeight: 700 }}>{s.label}</Typography>
</Stack>
<Typography variant="body2" sx={{ mt: 0.5, fontWeight: 600 }}>{s.value}</Typography>
</Paper>
</Button>
))}
</Box>
</PageSection>
<Paper sx={{ p: 2, borderRadius: 3 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Typography variant="subtitle2" sx={{ fontWeight: 800 }}>{t("workspaceRecentActivity")}</Typography>
<Button size="small" onClick={onReload}>{t("workspaceRefresh")}</Button>
</Stack>
<Divider sx={{ my: 1 }} />
<PageSection surface title={t("workspaceRecentActivity")} actions={<Button size="small" onClick={onReload}>{t("workspaceRefresh")}</Button>}>
{overview.recentActivity.length === 0 ? (
<Typography variant="body2" color="text.secondary">{t("workspaceNoActivity")}</Typography>
) : (
<Stack spacing={0.75}>
{overview.recentActivity.map((a, i) => (
<Stack key={i} direction="row" spacing={1} justifyContent="space-between">
<Typography variant="body2"><strong>{a.type}</strong>{a.detail ? ` — ${a.detail}` : ""}</Typography>
<Typography variant="caption" color="text.secondary">{new Date(a.at).toLocaleDateString(language === "nb" ? "nb-NO" : "en")}</Typography>
<Stack key={i} direction={{ xs: "column", sm: "row" }} spacing={0.5} justifyContent="space-between">
<Typography variant="body2" sx={{ overflowWrap: "anywhere" }}><strong>{a.type}</strong>{a.detail ? ` — ${a.detail}` : ""}</Typography>
<Typography variant="caption" color="text.secondary" sx={{ flexShrink: 0 }}>{new Date(a.at).toLocaleDateString(language === "nb" ? "nb-NO" : "en")}</Typography>
</Stack>
))}
</Stack>
)}
</Paper>
</PageSection>
<OverviewDetails jobId={overview.id} overview={overview} onReload={onReload} onEdit={onEdit} />
</Stack>