## Outcome - Audited the exact baseline `019c6c09721844ea6e08b2cd975fc543d424e617` on `main`/`origin/main`. - Working tree was clean; `git diff --check` passed. - No files were created or modified. - Enumerated the focused Playwright surface: **59 tests across 6 relevant specs**. No application tests were executed because this was a read-only inventory. ## Ownership map - **Rendered shell/header/navigation:** `scripts/health/dashboard_v5/render.py:120-165` - Static global header owns title, Today action, global search, privacy, and More. - Context range toolbar is rendered once, initially hidden (`render.py:128-131`). - **Primary panel visibility and tab state:** `dashboard-v5.js:1243-1267` - `selectView()` is the central visibility owner. - Main navigation history is handled at `dashboard-v5.js:1311-1321`. - **Global range:** `dashboard-v5-range.js` - Sole range state, parsing, URL synchronization, controls, subscriptions, and range `popstate`. - Range is visible only for **Cockpit, Explorer, Nutrition** (`dashboard-v5.js:1246-1249`). - Hidden for **Today, Akte, Calendar, Day, More**. - **Record/document route:** `dashboard-v5-record.js:98-123,536-594` - Canonical URL: `?view=record&tab=[&document=][&preview=extracted]`. - Record filters/search text stay in `history.state`, not the URL. - **Day/calendar route:** `dashboard-v5-day-controller.js:39-51,143-166,258-265` - Day: `?view=day&date=YYYY-MM-DD`. - Calendar: `?view=calendar`. - **Nutrition mapping route:** `dashboard-v5.js:671-689,737-749` - Canonical URL: `?view=nutrition&mapping=<32-hex>`. - Invalid/unknown mapping keys normalize to `?view=nutrition`. - **Global search:** `dashboard-v5-global-search.js` - Always remains in the shared header; query text never enters the URL. - Delegates navigation to Explorer, Record, or Day owners. ## Route/history risks and likely regressions 1. **Known current regression: mapping Forward restoration** - `docs/test-results/sprint6g-a3-integration-gate-20260717.json:22-40` records an accepted exception. - Browser Forward restores the mapping URL and selected card, but the `
` remains closed. - Existing failure evidence confirms `data-mapping-selected=true` with `open=false`. - Likely cause is competing `popstate` listeners triggering repeated Nutrition rendering plus asynchronous `requestAnimationFrame` open/focus work. 2. **Multiple independent `popstate` listeners** - Range, main shell, day/calendar, and record each register one. - They currently coexist by narrow route checks, but contextual-header work could easily add another render/history owner and recreate the earlier router competition. 3. **Frequent `url.search = ''`** - Present in primary navigation, range synchronization, record, day, calendar, Explorer metric opening, and mapping. - Any header implementation that writes history can erase: - `mapping` - `document` / `preview` - record tab/filter restoration state - day date - range/metric state. 4. **Range and mapping URLs are intentionally disjoint** - Range parser allows `view/from/to[/metric]`; mapping parser allows only `view/mapping`. - Changing the range while a mapping route is active rewrites the URL to range form and drops mapping selection. - B1 should not silently broaden or combine these URL contracts. 5. **More action does not currently persist** - `#more-toggle` calls `selectView("more")` without a history write (`dashboard-v5.js:1352-1356`). - Reload/Back behavior differs from primary navigation and could become visible if More is promoted in the contextual header. 6. **Record entry depends on event ordering** - Clicking Akte is routed by the record client’s button listener; the generic nav handler deliberately has no Doctor branch. - Replacing buttons or rebinding the header/nav could leave `view=doctor` rather than canonical `view=record&tab=overview`. ## Mobile CSS inventory Key rules are all in `dashboard-v5.css`: - Bottom navigation becomes fixed below `900px`: `194-201`. - Compact mobile nav labels and one-column cards below `560px`: `203-227`. - Full-screen global search and fixed custom-range panel: `379-387`. - Record tabs, document timeline/viewer, and sticky tools: `403-447`. - Mapping form one-column layout and wrapped range toolbar: `461-487`. - Calendar/day 390px overflow and 44px controls: `317-333`. - Existing risk: fixed bottom nav plus fixed custom-range panel/header growth can obscure content despite page-level overflow tests passing. ## Focused test groups Relevant existing isolated groups in `tests/browser/run_v5_isolated_matrix.sh`: - `a3` — 5 tests: contextual range/search, mapping/document state, keyboard URL persistence. - `a1` — 11 tests: mapping entry, record-to-Explorer return, desktop/mobile mapping/calendar. - `explorer` — 14 tests: range URL/history and global search. - `calendar` — 11 tests: day/calendar direct links and Back/Forward. - `record` — 14 tests: record/document router and mobile. - `record-files` — 4 tests: real synthetic original/viewer and mobile layout. ## Exact focused implementation plan 1. **Keep one visibility owner** - Extend `selectView()` in `dashboard-v5.js`; do not add a second panel/header router. - Add a pure contextual-header updater that changes DOM text, visibility, ARIA, and actions only—never URL/history. 2. **Add stable semantic header slots in `render.py`** - Context eyebrow/title/subtitle/action container. - Preserve existing global search, privacy, More, Today/day controls, stable IDs, and navigation markup. 3. **Let route owners publish context** - Main view owner publishes base view context. - Record client publishes tab/document/preview context after `activate()` and history restoration. - Day/calendar owner publishes day/date/calendar context. - Mapping renderer publishes selected mapping context. - No new `popstate` listener solely for the header. 4. **Do not change URL contracts in B1** - Preserve range, mapping, record/document, day, and calendar canonical forms exactly. - Header updates must leave `location.search` and `history.state` byte-for-byte unchanged. 5. **Fix the known mapping restoration race** - Make mapping restoration idempotent and synchronous after the final Nutrition render. - Ensure exactly one selected card is open and focused after reload, Back, and Forward. - Avoid stacked `requestAnimationFrame` callbacks acting on replaced nodes. 6. **Mobile CSS** - Add only scoped contextual-header layout rules. - Verify wrapping, 44px targets, no horizontal overflow, and no intersection with the fixed bottom nav/custom-range panel at `390×844`. 7. **Add focused B1 tests** - New `tests/browser/dashboard_v5_sprint6g_b1.spec.js`. - New `b1` isolated runner branch using the `record-files` synthetic profile. - Cover: - header context for every primary view; - range visibility matrix; - global search availability/Ctrl+K; - mapping URL/reload/Back/Forward/open/focus; - document preview URL/reload/Back/Forward; - record filter restoration; - day/calendar date routes; - 390px overflow, target sizes, and nav/content intersection; - no external requests, console errors, or extra session bootstrap. 8. **Focused verification only** - Python: new B1 render contract plus the existing A3 contextual markup and 6E.4A route/profile tests. - Browser: `b1`, then existing `a3`, `explorer`, `record`, and `calendar` groups as targeted compatibility gates. - No full Python suite or full 13-group browser-matrix recommendation for this bounded change.