Files
arah2/.planning/phases/07-hardening-private-launch/07-02-SUMMARY.md
T
araharah-executor 45bab7bcf9 docs(07-02): complete landscape + failure-state matrix plan — SUMMARY
- landscape geometry block + FM1-FM7 matrix + capture legs, 3/3 tasks
- 4 documented deviations (banner fit, chrome offset 113px, bottom register, no-geometry guard)
- verification: web 165/165, theme audit AUDIT CLEAN, capture 22/22 non-blank
2026-10-25 04:22:30 +08:00

149 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 07-hardening-private-launch
plan: 02
subsystem: ui
tags: [landscape, media-query, maplibre, failure-states, telemetry-rams, css-tokens, playwright-capture]
# Dependency graph
requires:
- phase: 06-turn-by-turn-guidance-live-driving
provides: the guidance cockpit (banner/strip/list/cards in shell.css), the pure D-04 reducer + replay fixtures, the ?drive= capture legs
- phase: 07-hardening-private-launch plan 01
provides: the fullscreen capability layer + safe-area watch (watched by FM6's old-client vector)
provides:
- "@media (orientation: landscape) and (max-height: 560px) geometry block in web/src/shell.css (tokens-only, sanctioned-literal list extended)"
- "web/test/failure-state-matrix.test.mjs — FAILURE_VECTORS data table + FM1..FM7 named cases pinning every designed failure surface"
- "route.ts no-geometry guard: a <2-point geometry is refused before the draw path (pending card / reroute-failed)"
- "capture.mjs per-leg viewport + two landscape legs (shell-guidance-landscape.png, shell-route-pending-landscape.png)"
affects: [07-06 device matrix (landscape QA legs), SC2 verification, future cockpit geometry work]
# Tech tracking
tech-stack:
added: []
patterns:
- "Landscape as CSS geometry adaptation only — portrait stays default, the map cartography untouched (D-03/D-10)"
- "Failure-state matrix as data — named vectors with trigger/expect tokens (RESEARCH Pattern 3)"
- "Type step-down inside a media query rides the SAME type-token scale (no new sizes)"
key-files:
created:
- web/test/failure-state-matrix.test.mjs
modified:
- web/src/shell.css
- web/scripts/capture.mjs
- web/src/route/route.ts
key-decisions:
- "Landscape banner shortened by tightening padding (xs) + one type-token step down (telemetry-num → headline-md, headline-md → label-lg) so the content actually fits the 72px floor the strip literal rides on"
- "Chrome-offset literal corrected to 113px (= the list margin-top's own stack: sm + 72 + 1 + 24 + sm) instead of the plan's 104px"
- "Bottom register keeps the portrait 64px control-clearance in landscape (measured: it already seats the card inside the landscape height and clears MapLibre's attribution + recenter)"
patterns-established:
- "FM* prefix joins the HS*/GS*/RS* suite convention for named-vector gates"
- "Per-leg viewport in the capture harness (default byte-identical to the old hard-coded one)"
requirements-completed: [PX-04]
# Metrics
duration: 31min
completed: 2026-10-24
---
# Phase 7 Plan 02: Landscape Geometry + Failure-State Matrix Summary
**Landscape cockpit geometry via a tokens-only `@media (orientation: landscape)` block plus the FM1–FM7 failure-state matrix pinning every designed failure surface — with the `<2`-point-geometry draw gap closed in route.ts**
## Performance
- **Duration:** 31 min
- **Started:** 2026-10-24T19:50:21Z
- **Completed:** 2026-10-24T20:21:19Z
- **Tasks:** 3/3
- **Files modified:** 4
## Accomplishments
- `web/src/shell.css` carries exactly one `@media (orientation: landscape) and (max-height: 560px)` block: banner 72px floor with safe-left/right edge padding (`max()`), plate 40px/glyph 28px, strip riding under 72px chrome, `guide-list` 100dvh-bounded — CSS only, zero radius/shadow/blur/transition, no MapLibre rule; the theme audit stays `AUDIT CLEAN`
- `web/test/failure-state-matrix.test.mjs` (new): `FAILURE_VECTORS` as data (exactly 7 classes) + FM1–FM7 — the empty/error/timeout/no-geometry/off-road/old-client/location-timeout classes all assert DESIGNED surfaces (real pending card, calm REROUTING slot, denied/awaiting-pin states), never a blank
- FM4's RED gate exposed a real gap: a route whose geometry has fewer than two points reached the consult draw path (a half-drawn line). Closed in `route.ts` (consult + reroute ladder) — the pending card / reroute-failed degradation renders instead
- `web/scripts/capture.mjs` accepts a per-leg `viewport` and adds the two landscape legs; `CAPTURE OK — 22/22 non-blank`, both landscape shots visually verified (banner/strip/card geometry measured clean at 844×390: banner 8–81, strip 81+, card 159–326)
## Task Commits
1. **task 1: the landscape geometry block in shell.css** — `13ba0bb` (feat)
2. **task 2 (RED): the FM1..FM7 failure-state matrix suite** — `b577bc4` (test — FM4 failing by design)
3. **task 2 (GREEN): refuse <2-point geometries before the draw path** — `add860b` (fix — FM4 green)
4. **task 3: the landscape + failure-state capture legs** — `7536ce5` (feat)
## Files Created/Modified
- `web/src/shell.css` — the landscape geometry block + the extended sanctioned-literal header list
- `web/test/failure-state-matrix.test.mjs` — new: FAILURE_VECTORS data table + 8 named FM cases
- `web/src/route/route.ts` — the T-07-12 no-geometry guard (consult + reroute)
- `web/scripts/capture.mjs` — per-leg viewport + landscape guidance/pending legs + header/expected-array lines
## Decisions Made
- The plan's prescribed landscape banner (min-height 72px alone) did not hold: the banner's content (guide-top + distance + street rows) measured 95px, so the strip literal put the strip BEHIND the banner. Fixed inside the plan's own mechanism (CSS, tokens): vertical padding to `--spacing-space-xs` and the distance/street fonts one step down the same type-token scale — content now measures exactly 72px at 844×390.
- The plan's chrome-offset literal (104px) does not close arithmetically against its own list margin-top (sm + 72 + 1 + 24 + sm = 113px); corrected to 113px in both the max-height and the sanctioned-literal comment.
- The plan's bottom-register rule (`bottom: safe-bottom + sm`) collided with MapLibre's attribution chip at the viewport bottom; the portrait rule's sanctioned 64px control-clearance composite already seats the card inside the landscape height (measured card 159–326 vs attribution top ≈364), so the landscape override was dropped rather than re-machining a new literal.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] Landscape banner content overflowed its 72px floor (strip rendered behind the banner)**
- **Found during:** task 3 (the landscape capture leg's screenshot + DOM probe)
- **Issue:** The planned block shrank only the plate (48→40px); the measures column (28px/32px telemetry distance + 20px/26px street) kept the banner at 95px while the strip's top literal assumed 72px — a real overlap (PX-04.e violation)
- **Fix:** CSS-only, tokens-only: banner vertical padding → `var(--spacing-space-xs)`, `.guide-distance` → `var(--type-headline-md)`, `.guide-street` → `var(--type-label-lg)` (letter-spacing tokens paired). Banner now measures exactly 72px (73 with its 1px separator border) at 844×390
- **Files modified:** web/src/shell.css
- **Verification:** DOM probe (banner bottom 81 = strip top 81) + re-captured landscape shot visually verified
- **Committed in:** 7536ce5 (task 3 commit; shell.css part of the same correction chain)
**2. [Rule 1 - Bug] The plan's 104px chrome-offset literal does not close against the list's own margin-top (113px)**
- **Found during:** task 1 acceptance review
- **Issue:** max-height `100dvh - 104px` with margin-top `safe-top + 113px` lets the fully-open list run 9px past the viewport bottom
- **Fix:** max-height corrected to `100dvh - 113px - safe-top - safe-bottom`; sanctioned-literal comment updated (104 → 113)
- **Files modified:** web/src/shell.css
- **Verification:** arithmetic closes; audit clean
- **Committed in:** 7536ce5
**3. [Rule 1 - Bug] The plan's bottom-register rule collided with MapLibre's attribution chip**
- **Found during:** task 3 (screenshot + DOM probe: card bottom 382 vs attribution top ≈364 at 844×390)
- **Issue:** `bottom: safe-bottom + sm` removed the 64px control-clearance the portrait rule provides; the card's bottom-left corner sat on the attribution chip
- **Fix:** dropped the landscape bottom-register override — the portrait rule's sanctioned 64px clearance already seats the card inside the landscape height (159–326)
- **Files modified:** web/src/shell.css
- **Verification:** DOM probe + re-captured shots (cockpit and pending card both fully visible, nothing clipped)
- **Committed in:** 7536ce5
**4. [Rule 2 - Missing Critical] A `<2`-point geometry reached the consult draw path (T-07-12)**
- **Found during:** task 2 (FM4 RED gate)
- **Issue:** `consult()` only checked `options.length === 0`; a 1-point option (the http-source validator admits `points.length >= 1`) would have drawn a degenerate half-line
- **Fix:** consult filters degenerate options into the pending-card class; the reroute ladder maps a no-drawable-option answer to the honest reroute-failed degradation. (The ?g= resume path in main.ts already had this guard.)
- **Files modified:** web/src/route/route.ts
- **Verification:** FM4 green; whole web suite 165/165; `npm run web:typecheck` clean
- **Committed in:** add860b (GREEN)
**Total deviations:** 4 auto-fixed (4 × Rule 1/2 bugs-gaps in the prescribed geometry + draw path)
**Impact on plan:** All fixes serve the plan's own binding laws (non-overlap, tokens-only, refused-before-draw). No scope creep; no behaviour redesign.
## Issues Encountered
- The plan's acceptance grep `border-radius|box-shadow|backdrop-filter|transition:|animation:` returns 4 on shell.css — these are the file's PRE-EXISTING sanctioned `border-radius: 0` resets (MapLibre chrome pin, recenter, route-chip; baseline verified identical via `git show HEAD`); the landscape block adds zero banned declarations.
- Self-referential grep trap in FM6: asserting the file does NOT contain "telegram/fullscreen" made the file contain it — pattern split across a concatenation.
## Known Stubs
None — every failure surface asserted is the real, data-wired designed state.
## User Setup Required
None — no external service configuration required.
## Next Phase Readiness
- PX-04.e's automated half is complete (landscape geometry + on-film evidence); 07-06's device matrix walks the physical landscape/fullscreen legs against these shots
- SC2's failure classes are pinned as named vectors; the verifier can rerun `npm test -w web` for the matrix
- Web suite baseline moved 157 → 165 (8 FM cases); STATE.md progress untouched per the orchestrator's instruction
---
*Phase: 07-hardening-private-launch*
*Completed: 2026-10-24*
## Self-Check: PASSED
All four artifact files exist; all four task commits found in git log; the sanctioned-literal grep returns 1; FAILURE_VECTORS declared once as data with 7 entries (FM1 asserts ids); the DOM-stub finally guard present (4). STATE.md and ROADMAP.md untouched (mtimes predate executor start).