Files
jobtrackingapp/docs/redesign/phase-4/p4-04/README.md
T

26 KiB
Raw Blame History

P4-04 — Page layout and typography primitives

Implemented and locally validated on 2026-09-12 from 806eee4e1ea44cd15c75cc73467b1101a7bd8d2c on codex/ui-redesign-phase4. Settings (/settings) is the only representative consumer. The commit containing this record is the P4-04 checkpoint (git log -- docs/redesign/phase-4/p4-04). P4-05, Phase 5 migration and B07 have not begun; no production deployment was performed. F04 remains open.

Approved sources: P4-04 scope, design system, component architecture, responsive strategy, accessibility plan, preservation impact and the 57-contract baseline.

Selection and implementation boundary

Settings exercises heading/description, independent sections/actions, bounded reading width, desktop tabs/mobile selector, preferences, loading/error and permission-dependent controls. Its subcards have no other production callers. Existing save handlers and endpoint contracts do not require refactoring. Inspection found an h5 shell title, non-heading section labels, nested padding and 240px minimum form widths. Retain the existing route, tab state/unmount behavior, independent requests and all controls. The shell opts only this route into the new header/width contract; other pages retain their existing presentation.

Use current Archivo/system fonts, semantic palette and focus treatment. Introduce opt-in MUI page-title/section-title/description variants rather than globally resizing legacy headings. Alias bounded reading/data/editor widths beside the existing theme gutter/section variables. Consolidate repeated section presentation into a small PageSection; it does not own requests, drafts or saves. Settings has no page-wide transaction: keep its save actions in their existing sections, with no invented global Save. Test the shared optional header action layout with a non-production component fixture.

Preservation impact recorded before implementation

All IDs retain their complete Phase 1/2 contracts. The following are directly affected or contained within the Settings consumer; no source/resource or permission semantics change.

PRES IDs Existing behavior → P4-04 change Existing automated evidence / additional proof
E07, E08 Same route, sidebar, header utilities, theme and EN/NB → opt-in semantic h1 and theme typography; responsive page/section rhythm sidebar-active-nav, theme-state/contrast, i18n-provider, P4-01/02 browser; add real Settings loaded/reflow/keyboard evidence
S03, J05, W11 Independent table/theme/language/rules/notification settings → same controls within shared layout settings-view (failed save/retry, scenario payload); redesign-preservation browser (resize draft, native save); add table/theme callback proof before edits
S04 AI privacy, usage, billing availability → same state owners in named sections settings-view, ai-usage-card, server privacy/entitlement tests; loaded zero-usage/locked and failure browser states
S05 Export/encrypted backup/deletion safeguards → section heading/action wrapping only account-data-export and server export/backup/deletion suites; browser no-data export and disabled deletion access
J10, J04 CSV/JSON tools and capture bookmarklet → retained nested controls and headings csv-export, capture tests and existing CSV browser; no serializer/capture edits
M06 Connected account navigation → action retained in its section email-provider-connections and new actual Settings navigation check; provider connectivity remains unverified
E09 Account identity governs all requests/caches → unchanged private tree and B02 context account-isolation browser and server session-boundary regressions

High-risk saving and lifecycle behavior already has focused tests. Before presentation changes, the added independent table/theme callback case and the existing Settings, usage, CSV and account-export cases passed: 13 tests in four suites. The final checkpoint adds presentation/real-browser evidence rather than rewriting these owners.

Shared architecture and changed files

  • PageLayout.tsx: two small compositions, PageHeader and PageSection, and the PageWidth union. Header text wraps without truncation; optional actions wrap independently. Sections have generated heading IDs and semantic section/h2/h3 structure. Neither headings nor noninteractive sections acquire tab stops. A section is plain by default; Settings explicitly requests the existing semantic paper surface/base theme radius. No mandatory nested card, data fetching, form state or new provider is introduced.
  • theme.ts: opt-in pageTitle, sectionTitle, pageDescription MUI typography variants and three page-width CSS aliases. Existing palettes, focus overrides, legacy typography variants, Archivo/system font stack, provisional hue and font hosting are unchanged. mui-theme.d.ts replaces theme.d.ts: the old declaration filename collided with theme.ts and TypeScript omitted it from the compilation. Giving it a distinct name enables the new variant augmentation while retaining P4-01 palette declarations. No tsconfig weakening or as any workaround was added.
  • App.tsx: only the Settings description and opt-in pageLayout="reading" prop for /settings and its existing trailing-slash equivalent. Query strings, route declarations, auth/providers, global actions and navigation handlers are untouched.
  • AppShell.tsx: the optional page-layout prop selects shared width/header styling. The existing main/gutter, toolbar, breadcrumbs, sidebar, drawer, user menu and their handlers remain. Other routes still use their old width and title markup. Connected Accounts is not migrated by the /settings opt-in.
  • SettingsView.tsx: remove local SectionCard duplication and the second outer padded Paper; use shared section spacing, headings and shrinkable preference columns. Preserve desktop tabs/mobile selector and their tab state. The desktop tablist gains a translated name distinct from the existing mobile selector. Connected Accounts uses the shared section-header action slot, wrapping below the description on narrow screens; its route/callback is unchanged.
  • Settings-only subcomponents AiPrivacySettingsCard, AiUsageCard, QuickCaptureCard, RulesSettingsCard, BackupCard and nested ImportExportJobs: replace repeated section presentation/heading markup. Usage plan metadata occupies the header's trailing slot. No API call, payload, entitlement, download, serialization, bookmarklet, confirmation or save implementation changes.
  • i18n/translations.ts: one truthful Settings description in English and Bokmål. No new font or final branding choice.
  • Tests: new src/page-layout.test.tsx, one additional src/settings-view.test.tsx case and new e2e/settings-page-layout.spec.ts. The inherited e2e/redesign-preservation.spec.ts Settings case now explicitly awaits/asserts the notification PUT before its existing readback/reload checks; the separately discovered SQLite contention is documented below. Documentation: this record/evidence, Phase 4 index, Phase 2 trace link and active TODO status.

Typography and rhythm

Role Opt-in value / responsibility
Page title h1 semantics; 24/30 px equivalent on phone, 28/34 from 768px; weight 700; rem sizes
Section title h2/h3 chosen by hierarchy; 18/24 px equivalent, weight 600
Description 14/20 px equivalent, weight 400, semantic secondary text; at most 75ch; long words may wrap
Reading width 60rem, matching Settings' existing nominal 960px cap; header and body now align
Data/editor aliases 82.5rem / 100%; future opt-in contracts only. No table/editor migrated or certified here
Page gutters Existing 16/24/32px theme variables; owned once by the shell
Sections Existing 24px section gap; 16px header/content gap; optional surface uses existing 16/20px card padding and base theme radius

No global body/legacy-heading resize, new palette, motion, density preference or competing token system. The first visual review caught the MUI numeric radius multiplier creating overly rounded new sections; using the existing base radius corrected that before the complete checkpoint. Section grid alignment also avoids distributing spare vertical space inside unequal-height columns.

Regression and visual method

The two component tests prove full localized heading names/semantic levels, independent actions, disabled actions, nonfocusable sections and unmodified ViewStateNotice loading/error/retry/empty transitions. The new Settings callback test protects separate table-size, column and theme changes without backend preference writes.

Four real Chromium workflows cover light/English, light/Bokmål, dark/English and dark/Bokmål, each through desktop and phone. Each creates its own non-admin account in the disposable local API database, uses real authentication/B02 context and blocks outbound browser traffic. No production/provider credentials or services are used.

Coverage includes:

  1. Loaded real Settings: one h1, named section headings, actual zero-usage Free state and unavailable external AI. The zero-usage state is not a simulated provider success.
  2. 1920/1440/1024/768/767/390/320px General reflow; 320/390/768/1024/1440px rule editing without losing the draft. The overflow oracle checks actual ordinary descendants against the main/viewport edges, so the inherited body overflow rule cannot disguise clipping.
  3. A synthetic long English/Bokmål title at 320px to stress wrapping. Only title text is substituted in the test; no CSS injection, hidden controls or production fixture route. Reload restores actual copy.
  4. Keyboard mobile-section selection and focus return. Existing initial rules error/re-entry recovery, followed by native scenario save; the native API confirms the changed value.
  5. Held notification read/skeleton in the same page layout; one injected local 503 save failure preserves the unchecked draft; explicit retry persists it and reload proves it remains saved. Initial validation showed that error clearance alone was insufficient save proof because it occurs before request completion. The permanent oracle waits for the exact PUT response and checks its status before reading persisted state; the separate backend contention evidence is retained below.
  6. Backup headings/actions at 320px, actual no-job account ZIP download and server-disabled deletion. Existing unit/server tests retain recent-auth, exact confirmation and enabled deletion coverage; no destructive browser operation is attempted.
  7. Real Connected Accounts navigation through the responsive section action, inherited keyboard outline, and continued non-admin API denial. Provider connection functionality itself remains under its existing tests and operational gap.

The shared header's optional page action uses the same composition as the real Connected Accounts section action. Settings has no artificial top-level Save button. Component tests also exercise a long localized page-action label. All screenshot data is synthetic; authenticated traces and databases remain ignored.

Intentionally retained debt and Phase 5 implications

  • This is a single Settings-page migration. Secondary navigation reorganisation, moving account/security sections, new settings routes, shell/sidebar redesign, command expansion and the other pages remain Phase 5+ work. P4-05 density has not begun.
  • Initial notification/AI-privacy load failures still use their existing error/skeleton behavior; no new retry or completion-announcement contract is claimed. Rules reload through existing section re-entry. Unsaved rules still unmount when deliberately changing sections, as recorded in Phase 2; viewport changes retain them. Shared layout must not silently invent draft persistence.
  • Existing import file-label keyboard semantics, full JSON roundtrip, encrypted restore, enabled paid-account deletion, provider connectivity/billing and real delivery require their separately scoped evidence. The CSV serializer and B01–B05 controls are unchanged.
  • Existing P2-D01 expected failure, P4-02-D01–D04 and P4-03-D01 remain explicit. CV margins/templates/pagination correctness is dedicated later work. No unrelated accessibility suppression or defect fix is included.
  • P4-04-O01 — existing fixed-toast overlap: visual review observed the export-success toast covering the bottom Connected Accounts action briefly. toast.tsx and its placement/timing are unchanged. The final fixture waits for normal dismissal, then checks the focused button receives hit testing before capture. This is not a claim that all transient toast overlap is repaired; review shared feedback placement with the later shell work.
  • P4-04-O02 — existing SQLite notification-save contention: concurrent request evidence and investigation are recorded below. The backend and application save handler are unchanged. Do not claim concurrent preference writes/reads are reliable based on the sequential preservation fixture; investigate in a separate backend reliability ticket without folding it into P4-05.
  • Browser viewport/keyboard and axe evidence do not certify Narrator/NVDA, real mobile touch/software keyboards, screen-reader behavior, browser 200%/400% zoom or forced colors. Those remain manual validation gaps. Data/editor width aliases still need their actual consumer fixtures before adoption.
  • F04 remains open: no deployed ingress, firewall, DNS or TLS evidence was collected. Feature-branch push does not deploy or close it.
  • Phase 5 can opt each reviewed consumer into the header/width/section contract, preserving its domain state and adding its own tests. Do not enable the new header globally or migrate the provider tree as a side effect.

Rollback is limited to this presentation/test slice and restoring the declaration filename if reverting the entire change. There is no schema, storage format, package, CI, infrastructure or backend-contract change.

Final verification

Validation Inherited P4-03 baseline P4-04 result
Backend Release regression 916 916 passed, zero failed/skipped
Frontend Jest 345 348 passed in 74 suites: two new shared-composition cases and one independent Settings-preference case
Full Chromium suite 43 actual passes + P2-D01 expected failure 47 actual passes + unchanged P2-D01 expected failure: four new Settings workflows, no inherited test removed or reclassified
Final focused Settings repeat Not additional cases 4 passed, the same four cases repeated after strengthening post-scroll focus hit testing
Account/IP rate-limit HTTP 9 9 passed
Production-mode ingress HTTP 6 6 passed locally; does not close deployed F04
Shared PDF admission HTTP 1 1 passed
Effective Compose/config adversarial checks 3 3 passed
TypeScript Pass Pass, standalone frontend and the additional scoped E2E type check
ESLint Pass Pass, zero warnings allowed
Frontend production build Pass Pass, Next static production export used by the browser runs
API Release build Pass Pass, zero warnings/errors with -warnaserror
Additional repository checks Existing gates retained Supply-chain tests 3, Ollama evaluation harness tests 5, extension tests 3, all passed
Repository hygiene Pass Tracked-secret scan, working/staged whitespace and local Markdown targets passed; 120 current/root Markdown documents checked, historical archive excluded

The full backend suite includes the existing B01 destination, B02 account/session, B03 admission, B04 partition and export coverage. The frontend/browser suites retain B02 switching, CSV safety and the existing CV/public-PDF flows. No application, backend, package, CI or infrastructure change followed these baseline runs. Browser-only refinements strengthen response synchronization and screenshot/focus proof. The final complete browser run uses both the new fixture and the explicitly sequential inherited Settings check described below.

Playwright's list summary counts the expected failure as a successful test outcome. The JSON SCAN_RESULTS.json explicitly records 47 passed → passed and one failed → failed, not 48 successful workflows. P2-D01 still reaches the existing SQLite submitted-package failure; its historical-binary follow-on proof remains blocked.

Rendered review and accessibility evidence

The final four Settings cases produced 36 synthetic screenshots, 60 geometric reflow checks with zero out-of-bounds nodes, and 20 full tagged axe scans with zero violations and no rule/element exclusions. All 20 scans contain incomplete contrast checks; aria-hidden-focus also appears as incomplete. These remain review items, not automated passes. Raw incomplete details are retained in SCAN_RESULTS.json. Representative rendered images were inspected for hierarchy, density, wrapping, semantic surfaces, secondary-text readability and unclipped keyboard focus in both themes/locales.

PRES IDs → preserved behavior Automated evidence Representative rendered evidence
E07/E08 → Settings route, theme/language and existing navigation Four new browser cases: one h1; EN/NB; 320–1920px reflow; keyboard section selection/focus return; non-Settings route retained Desktop light/NB, desktop dark/EN, phone dark/EN, phone light/NB
S03/J05/W11 → independent preferences, rule draft survives resize, native save/retry/reload New preference unit case; Settings browser API readback; inherited settings-view/redesign-preservation coverage Long NB heading, rules error dark/NB, notification skeleton light/EN, failed save dark/EN
S04 → real zero-usage Free state, external AI remains disabled Actual native account/usage response, disabled switch; existing ai-usage-card/privacy/entitlement cases Usage and capture at 320px; this is not provider or paid-plan certification
S05/J10/J04 → account export, deletion gate, existing import/capture Actual ZIP download and disabled deletion; unchanged CSV/capture unit/backend tests Backup light/NB, backup dark/EN, export controls desktop
M06/E09 → Connected Accounts action, session-owned API access Keyboard activation opens existing route, focus outline/hit-test, non-admin API 403; existing account-isolation/provider tests Connected action light/EN, Connected action dark/NB

All screenshots use 900px height; widths are in names or 320px for error/loading/backup/connected/long-heading captures. The remaining combinations are in visual/. Existing source controls and the mobile section chooser remain; no action is hidden to pass reflow. Post-scroll hit testing uses retrying assertions to wait for the rendered position to settle. The initial immediate assertion sampled before that stable position; no application workaround or style injection was added.

This validates the shared primitives and the single Settings consumer, not every application's visual parity. Reading width is exercised here; data/editor aliases are reserved for separately validated consumer migrations. Manual assistive-technology and the explicitly retained debt above remain open.

P4-04-O02 investigation and test-boundary correction

A complete rerun failed the inherited [PRES-S03 PRES-W11] responsive settings navigation keeps rule edits and persists notification preferences case. Its native readback remained true; the browser had an unchecked draft and a visible save-error alert. The trace proved the click reached the API: PUT /api/notification-settings returned 500, while the test's overlapping GETs returned 200 with the old value. This was neither a lost click nor a successful save hidden by presentation.

Repeating that unchanged fixture ten times with API console capture yielded eight passes and two failures. Both server failures were DbUpdateException with SQLite error 5, database is locked, through UserStore.UpdateAsync and NotificationSettingsController.Put. This controller, its Identity/database configuration and the Settings save handler are unchanged from 806eee4e. The original trace is .artifacts/p4-04-settings-failure-trace.zip; the failing full run is preserved as p4-04-browser-intermittent.json/.log, and the repeated API diagnostics as p4-04-settings-diagnostic.json/.log. These synthetic traces/logs remain ignored rather than publishing session credentials.

The inherited test began native GET polling immediately after clicking Save, before the PUT completed. That added concurrent read traffic unrelated to its sequential UI preservation contract. It now awaits the exact PUT and requires HTTP 200, then retains the existing native readback and reload checks. No 500 is accepted, retried or converted to an expected failure. P2-D01 is unchanged. The four new Settings cases already await their PUT responses and explicitly verify error/draft/retry behavior.

The corrected sequential case passed ten of ten repeats, with API console capture and no notification-save 500. These are diagnostic repeats of one inherited case, not ten new tests. The subsequent complete suite is the final checkpoint result in the table above.

This correction isolates the intended sequential test; it does not repair or dismiss the backend contention. Further work must reproduce the concurrent API condition, identify the locking/transaction cause and verify a bounded backend solution. No schema, retry policy, SQLite mode or server synchronization change is included here. The original fixture is recoverable from 806eee4e:job-tracker-ui/e2e/redesign-preservation.spec.ts for the same ten-repeat local probe.

Commands

Run from repository root unless a directory change is shown. Output logs/TRX/JSON are retained under ignored .artifacts/; the scan evidence and synthetic screenshots are committed here. Existing installed dependencies were used; there were no CI/package/infrastructure changes or remote-provider calls.

$env:DOTNET_ROOT='C:/Users/Connor/.dotnet'
& C:/Users/Connor/.dotnet/dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-restore --logger 'trx;LogFileName=p4-04.trx' --results-directory .artifacts/redesign-p4-04 -- xUnit.parallelizeTestCollections=false xUnit.maxParallelThreads=1
# Final repeat after the browser-only correction, using the unchanged Release build:
& C:/Users/Connor/.dotnet/dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --no-restore --logger 'trx;LogFileName=p4-04-final.trx' --results-directory .artifacts/redesign-p4-04 -- xUnit.parallelizeTestCollections=false xUnit.maxParallelThreads=1
& C:/Users/Connor/.dotnet/dotnet.exe build JobTrackerApi/JobTrackerApi.csproj --configuration Release --no-restore -warnaserror
python scripts/test-ingress.py
python scripts/test-supply-chain.py
python scripts/test-ollama-evaluation.py
node --test browser-extension/tests/*.test.mjs
Set-Location job-tracker-ui
npm test -- --watchAll=false --runInBand --json --outputFile=../.artifacts/p4-04-jest.json
npm run typecheck
node node_modules/typescript/bin/tsc --project ../.artifacts/p4-04-e2e-tsconfig.json
npm run lint
$env:NEXT_PUBLIC_API_BASE_URL='http://localhost:5302/api'
$env:NEXT_TELEMETRY_DISABLED='1'
npm run build
$env:DOTNET_HOST_PATH='C:/Users/Connor/.dotnet/dotnet.exe'
$env:PLAYWRIGHT_STATIC_EXPORT='true'
$env:PLAYWRIGHT_PREBUILT_EXPORT='true'
$env:P4_04_CAPTURE='true'
$env:PLAYWRIGHT_JSON_OUTPUT_FILE='../.artifacts/p4-04-browser.json'
node node_modules/@playwright/test/cli.js test --reporter=list,json
$env:PLAYWRIGHT_JSON_OUTPUT_FILE='../.artifacts/p4-04-final-focused.json'
node node_modules/@playwright/test/cli.js test e2e/settings-page-layout.spec.ts --reporter=list,json
# Diagnostic: executed before and after the inherited Settings response barrier;
# the first run reproduced O02. No retry option or expected failure was added.
$env:DEBUG='pw:webserver'
node node_modules/@playwright/test/cli.js test e2e/redesign-preservation.spec.ts --grep 'responsive settings navigation' --repeat-each=10 --reporter=list,json
Remove-Item Env:DEBUG
node node_modules/@playwright/test/cli.js test --config=playwright.rate-limits.config.ts
node node_modules/@playwright/test/cli.js test --config=playwright.ingress.config.ts
node node_modules/@playwright/test/cli.js test --config=playwright.pdf.config.ts
Set-Location ..
python scripts/supply-chain.py scan-secrets
git diff --check
git diff --cached --check

The ignored E2E type config extends the existing frontend configuration, includes the new Settings spec, the affected inherited preservation spec and MUI augmentation, disables incremental caching and resolves frontend Node types. It adds no test exclusion or production compiler change.