From efba4d6f4c42d22c551644ceeb2f46d3106bc3f0 Mon Sep 17 00:00:00 2001 From: araharah-executor Date: Sat, 24 Oct 2026 06:04:52 +0800 Subject: [PATCH] docs(05-05): complete verification-gates plan - BA9/BA10 + BA5 sentinel extension, five ?fixpair-raced capture legs (13/13), RP1/RP2 Worker privacy gates - two Rule-1 boot fixes (onLoad wiring + consult-after-load) documented - STATE.md and ROADMAP.md untouched (orchestrator-owned) --- .../05-routing-route-display/05-05-SUMMARY.md | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 .planning/phases/05-routing-route-display/05-05-SUMMARY.md diff --git a/.planning/phases/05-routing-route-display/05-05-SUMMARY.md b/.planning/phases/05-routing-route-display/05-05-SUMMARY.md new file mode 100644 index 0000000..0b3a164 --- /dev/null +++ b/.planning/phases/05-routing-route-display/05-05-SUMMARY.md @@ -0,0 +1,173 @@ +--- +phase: 05-routing-route-display +plan: 05 +subsystem: verification-gates +tags: [bundle-budget, build-artifact-gates, capture-harness, fixpair, state-tokens, privacy-gates, worker-boundary, playwright, tdd] + +# Dependency graph +requires: + - phase: 05-routing-route-display/01 + provides: "getPairOrigin (the pinned-public-origin lookup the ?fixpair knob resolves through) and the routing workspace sources BA9 measures" + - phase: 05-routing-route-display/02 + provides: "the committed 15-pair corpus (BA10's pairId set; the runtime demoPair for the capture legs; national-museum as the RT4 pending probe) and the 16.6 KB fixture payload inside BA9's budget" + - phase: 05-routing-route-display/04 + provides: "the ARAHARAH-ROUTE state tokens, the .route-chip surface, the route/pending cards, and the production build carrying the route family — everything the five legs race and assert" +provides: + - "BA9 — the D-11 bundle-budget gate: summed gz of fixtures + routing/src + web/src/route ≤ 204800 B, measurement PRINTED every run (35,076 / 204,800 measured)" + - "BA10 — pairId parity: every committed pairId must appear in the shipped JS assets (the BA7 analog; stale fixture-less bundles are unshippable)" + - "BA5 extension — ROUTE_PROFILE_HARNESS_ONLY joins the harness-leak scan; the route-profiles module is provably dead-code-eliminated from web/dist" + - "?fixpair= harness knob + web/src/mock/route-profiles.ts (harness-only pinned-origin lookup; pairId-only, never coordinates — T-05-14)" + - "Five route capture legs (8 → 13 expected artifacts): shell-route-fixture / shell-route-alt / shell-route-pending / shell-route-dark / route-gallery-compare — all raced on ARAHARAH-ROUTE state tokens + DOM text assertions" + - "RP1/RP2 in bot/test/route-privacy.test.mjs — the Worker-side no-route-proxy structural gates (D-05, T-05-16)" + - "[Rule 1] main.ts boot fixes: opts.onLoad → installRouteLayers wired, and the route consult gated on map load (the line actually paints now)" +affects: [phase-6-guidance (consumes the painted-route state machine + capture discipline), phase-verifier (SC3 evidence chain), future BA budget growth] + +# Tech tracking +tech-stack: + added: [] # zero new dependencies — node:zlib for BA9, playwright already present + patterns: + - "Budget gates print their measurement: a budget nobody sees is a hope, not a gate (BA9 console.log on every run)" + - "State-token-raced captures: the leg races ARAHARAH-ROUTE, parses state=, then asserts DOM text — signals carry state, never coordinates (D-15 runtime half)" + - "Harness knobs deliver committed public data only (?fixpair → getPairOrigin), never raw coordinates — the coordinate-in-URL surface stays closed (T-05-14)" + - "Teeth-proof RED for gates over clean trees: plant a violating scratch file, watch the gates fail, remove, watch them pass" + +key-files: + created: + - web/src/mock/route-profiles.ts + - bot/test/route-privacy.test.mjs + modified: + - web/test/build-artifact.test.mjs (BA9 + BA10 + BA5 sentinel + header inventory) + - web/scripts/capture.mjs (ARAHARAH-ROUTE signal, waitMode route, interact hook, 5 legs, 13-artifact gate, composeCompare refactor) + - web/index.html (?fixpair knob inside the __ARAHARAH_HARNESS__ block) + - web/src/main.ts ([Rule 1] onLoad wiring + consult-after-load gate) + - web/test/route-surface.test.mjs (RS2 boot-wiring regression gate) + +key-decisions: + - "BA9 measures SOURCE gz as a stand-in for minified gz — conservative in the gate's favor (minified gzips smaller) and the 200 KiB ceiling carries order-of-magnitude headroom" + - "The capture demo pair is selected AT RUNTIME from the committed corpus (find routes.length >= 2 ?? first) — never a hardcoded pair literal, so the legs track the dataset" + - "mapLayersReady gate in main.ts resolves in a finally block — a route-layer install failure degrades to 'no route drawn' without wedging the consult (cards-only boots resolve immediately)" + - "RP gates scan comment-stripped source with prefer-fail over-stripping (the no-storage.test.mjs pattern) so doc mentions can never whitewash a real proxy" + +patterns-established: + - "Capture-leg contract: race the state token, assert state=, poll DOM text, theme-slot honesty check on themed legs, interact(page) hook for pre-shot interactions" + - "Boot-wiring gates: RS gates over fakes cannot see a missing callback in the composition root — the new RS2 boot-wiring grep pins main.ts's onLoad wiring" + +requirements-completed: [RTNG-01, RTNG-02] + +# Metrics +duration: 19min +completed: 2026-10-23 +--- + +# Phase 5 Plan 5: Verification Gates — BA9/BA10 + Route Capture Legs + Worker Privacy Gates Summary + +**The phase closes on mechanical proof: BA9 prints the route payload at 35,076 / 204,800 gz bytes and BA10 proves every committed pairId rides the shipped bundle, five new capture legs race the ARAHARAH-ROUTE state token through the new ?fixpair knob (13/13 non-blank, the orange line verified painted), and RP1/RP2 gate the Worker out of the routing path — the amended SC3's "bundle-size and type gates holding" is now read from gate output, not memory.** + +## Performance + +- **Duration:** 19 min +- **Started:** 2026-10-23T21:44:12Z +- **Completed:** 2026-10-23T22:03:31Z +- **Tasks:** 3/3 +- **Files touched:** 7 (2 created, 5 modified) + +## Accomplishments + +- **D-11 is a gate, not a hope**: BA9 sums `gzipSync` over the fixtures dataset + every `routing/src` *.ts + every `web/src/route` *.ts (production-shaped by construction), asserts ≤ 204800 bytes, and PRINTS `BA9 route bundle budget: 35076 / 204800 gz bytes` on every run — the failure message carries the measurement and the overshoot. BA10 (the BA7 analog) asserts every one of the 15 committed pairIds appears in the concatenated `web/dist/assets/*.js` — a stale bundle that lost the fixtures is unshippable. BA5's harness-leak scan gained `ROUTE_PROFILE_HARNESS_ONLY`; the production build provably excludes the module (0 occurrences in web/dist — the `if (__ARAHARAH_HARNESS__)` dynamic import dead-code-eliminates, verified by grep after build). +- **The five route capture legs pass against the production-shaped harness bundle** (8 → 13 expected artifacts): fixture (state=drawn + destination name + MIN + 2000ms tile settle), alt (first `.route-chip` tap → `/ROUTE \d\/\d/` register — ROUTE 2/2, +1 MIN VS ROUTE 1 verified in the shot), pending (`state=pending` + "not wired up yet" — the honest D-03 card with the privacy clause, never a fabricated geometry), dark (PX-02 theme-slot honesty check — chrome re-slots, orange route + grayscale basemap Rams-constant), and the DSGN-02 route-gallery-compare beside `navigate_map_overview/screen.png`. All legs are data-driven: `demoPair` is picked at runtime from the committed corpus, and the destination name is read from the gazetteer at capture time. +- **The ?fixpair knob closes the coordinate surface it opens**: `?fixpair=` resolves through `getRouteProfileOrigin` → `getPairOrigin` — the ONLY origin source is the committed public corpus (D-12); a `?lat/?lng` knob is deliberately absent (T-05-14). Unknown pairId keeps the default fix — the boot never wedges. +- **D-05 is mechanically gated**: RP1 (zero `/api/route`, `registerRoute`, `routeProxy` in comment-stripped bot/src) and RP2 (zero `@araharah/routing` imports) — the Worker cannot route even accidentally. Teeth-proven: a planted violating file fails both gates; removed, the bot suite runs 74/0. +- **Two real bugs found and fixed by the new legs** (the plan's stated purpose for them): see Deviations. + +## Task Commits + +1. **task 1: BA9 + BA10 + BA5 sentinel extension** (gate file over already-shipped artifacts — no failing RED exists; mechanism teeth proven by a bogus-pairId probe returning false on the real bundle) + - `29ad451` test(05-05): BA9 bundle-budget + BA10 fixture-parity gates + BA5 harness-sentinel extension +2. **task 2: ?fixpair knob + five route capture legs** (strict RED→GREEN) + - `c9b1128` test(05-05): add five route capture legs racing ARAHARAH-ROUTE (RED) — the drawn legs failed exactly as predicted (default fix outside the demo pair's 200 m tolerance → state=pending) + - `af744e4` feat(05-05): ?fixpair harness knob + pinned-corpus origin lookup (GREEN) — includes the two Rule-1 boot fixes below +3. **task 3: Worker-side no-route-proxy gates** (gates over a clean tree; RED = planted-violation teeth proof) + - `6302901` test(05-05): Worker no-route-proxy gates RP1+RP2 (D-05) + +## Files Created/Modified + +- `web/test/build-artifact.test.mjs` — BA9 (measured, printed, budget-gated), BA10 (pairId parity with `pairs.length >= 10` real-dataset guard), BA5 token list + header inventory +- `web/scripts/capture.mjs` — `ARAHARAH-ROUTE` in SIGNALS/seen-map/diagnostics; `waitMode: "route"` (30s race, state= parse, expectText DOM polls, PX-02 honesty check, 2s settle); `interact(page)` pre-shot hook; the five legs; `composeCompare` shared composer; 13-artifact missing/undersized/failures gate +- `web/index.html` — `?fixpair` knob inside the harness block (dynamic import of the profile module; knob documented in the profile comment line) +- `web/src/mock/route-profiles.ts` — HARNESS-ONLY module: sentinel + `getRouteProfileOrigin`; D-12 + BA-exclusion header +- `web/src/main.ts` — [Rule 1] `bootMap(..., { onLoad: (m) => installRouteLayers(...) })` + the `mapLayersReady` gate ahead of the auto-consult +- `web/test/route-surface.test.mjs` — RS2 boot-wiring gate (the composition-root wiring the fakes could not see) +- `bot/test/route-privacy.test.mjs` — RP1/RP2 structural gates over bot/src + +## Decisions Made + +- **BA9 approximates minified gz with source gz** — conservative in the gate's favor at these sizes; documented in the gate's comment per the plan. +- **demoPair picked at runtime, destination name read from the gazetteer at capture time** — the legs track the dataset instead of hardcoding literals (the capture_contract's own snippet). +- **mapLayersReady resolves in `finally`** — an install failure must degrade to "no route drawn", never wedge the consult (the RS4 exception-path law applied to the boot gate). +- **RS2 boot-wiring gate added to the web suite** (a file outside the plan's `files_modified`) — Rule 1's "add a test if applicable" clause: the missing-callback bug was invisible to every fake-driven gate, so the wiring itself is now grep-pinned. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] main.ts never passed bootMap's opts.onLoad — the route line never painted** +- **Found during:** task 2 (first GREEN capture run: card + pin + camera all rendered, zero orange on the map) +- **Issue:** `bootMap(document.getElementById("map")!)` was called without the onLoad callback, so `installRouteLayers` never ran in the real boot; `setRouteDrawn`'s `getSource(...)?.setData(...)` silently no-op'd. Invisible to RS3/RS4 (recording fakes) and RS2 (map.ts textual position only). +- **Fix:** wire `onLoad: (m) => installRouteLayers(m as unknown as MapLike)`; add the RS2 boot-wiring gate pinning it. +- **Files modified:** web/src/main.ts, web/test/route-surface.test.mjs +- **Commit:** af744e4 + +**2. [Rule 1 - Bug] route consult raced the map load — the draw landed on sources that did not exist yet** +- **Found during:** task 2 (after fix 1 the line STILL didn't paint; a headless probe showed `ARAHARAH-ROUTE state=drawn` firing BEFORE `RAMS-BASEMAP-READY layers=115`) +- **Issue:** the harness session stubs resolve the location chain faster than WebGL/style init, so consult + setData ran before the load handler installed (empty) sources — the drawn data was discarded. The 05-04 assumption "the load hook precedes the location chain" is false under stubbed session latency (and plausible in production on fast networks). +- **Fix:** `mapLayersReady` promise resolved in the load handler after `installRouteLayers` (finally-resolved — install failure degrades, never wedges; cards-only boots resolve immediately); the auto-consult awaits it. Probe after: READY precedes ROUTE, and the orange line paints (verified in all three drawn captures). +- **Files modified:** web/src/main.ts +- **Commit:** af744e4 + +### Judgment calls within plan laws (not deviations) + +- Task 1 and task 3 gate artifacts that already hold (fixtures shipped in 05-02/05-04; the Worker tree is clean) — a failing RED run does not exist for them; both were teeth-proven instead (bogus-pairId probe for BA10's mechanism; planted violating file for RP1/RP2). Task 2's RED is genuine. +- The plan's inline `resolve(REPOROOT, ..., "utf8")` snippet misplaced a paren; implemented with the intended semantics (utf8 read of the resolved path). + +## TDD Gate Compliance + +- **task 2** is the plan's real RED→GREEN: `c9b1128` (legs fail: 3× state mismatch + missing gallery input, exit 1) → `af744e4` (13/13). The GREEN commit also carries the two Rule-1 fixes the RED run exposed. +- **tasks 1 and 3** carry `tdd="true"` but gate already-true invariants (the plan frontmatter is `type: execute`, so no plan-level RED/GREEN ladder applies). Teeth were demonstrated without polluting the tree: BA10's `includes()` discriminates on the real bundle (bogus pairId absent); RP1/RP2 fail against a planted violation and pass on the clean tree. No fake RED commits were made. + +## Known Stubs + +None. The `?fixpair` knob, the five legs, and both gate files are fully wired to committed data. (web/screenshots/* are regenerated gitignored artifacts by design — the capture harness's own contract.) + +## Threat Surface Scan + +No new surface beyond the plan's ``: the knob accepts pairIds only and resolves them exclusively through the committed public corpus (T-05-14 mitigated, runtime-proven by the pending leg); BA9 gives T-05-12 its build-gate tier (35 KB measured vs 204,800 allowed); the BA5 sentinel extension enforces T-05-15 (0 sentinel occurrences in web/dist); RP1/RP2 enforce T-05-16 (0 banned tokens in bot/src). The capture signals carry state tokens only — no coordinates in any console line (D-15). No threat flags. + +## Issues Encountered + +- Node lives under nvm; non-interactive shells need `source ~/.nvm/nvm.sh` first (all prior 05-xx plans noted the same). No project impact. +- A block-comment in the first draft of route-privacy.test.mjs terminated early on a `*/` inside `bot/src/**/*.ts` — caught by the suite loader as a syntax error, fixed by rewording. No gate semantics changed. +- Playwright's ESM resolution fails from /tmp scripts; the debug probe imported it by absolute path. The probe script lived outside the repo and was never committed. + +## User Setup Required + +None — no external service configuration. The captures run against the committed corpus and local builds; tiles come from the existing OpenFreeMap runtime fetch. + +## Next Phase Readiness + +- **Phase verification / the amended SC3 evidence chain is complete**: RT1–RT10 (corpus routes cleanly, corpus gate PASS) + BA9/BA10 (bundle-size gate, measurement printed) + the 05-01 C2 type gates + the pending capture (deliberate, designed) — all four clauses have a green instrument. +- **Phase 6 (guidance)**: the route state machine is now visually verified against the production bundle; the painted-route boot ordering (consult-after-load) is the exact precondition guidance's turn-card flow inherits; the capture harness's state-token race pattern extends to guidance signals. +- **Verifier hooks**: BA1–BA10, RS1–RS10 (+ boot-wiring), COPY gates, RT1–RT10, RP1/RP2, and the 13-leg capture gate are all mechanical. + +--- +*Phase: 05-routing-route-display* +*Completed: 2026-10-23* + +## Self-Check: PASSED + +- All created files verified present on disk (web/src/mock/route-profiles.ts, bot/test/route-privacy.test.mjs); all modified files verified in the commits. +- All 4 plan commits verified in git log: 29ad451, c9b1128, af744e4, 6302901 (task 2 strict RED→GREEN). +- `npm test -w web`: 76 passed, 0 failed — output contains `PASS BA9`, `PASS BA10`, `BA9 route bundle budget: 35076 / 204800 gz bytes`. +- `npm run web:capture`: exit 0, `CAPTURE OK — 13/13 non-blank captures`; the five new artifacts exist; the orange line visually verified in fixture/alt/dark shots; the pending card honest. +- `npm test -w bot`: 74 passed, 0 failed — `PASS RP1`, `PASS RP2`; tree greps for `/api/route` and `@araharah/routing` in bot/src both return 0. +- `npm run routing:test` 35/0; `npm run routing:corpus` PASS (gz 16,576 B; multi-route 8 / floor 4); web/bot/routing typechecks exit 0. +- STATE.md and ROADMAP.md untouched (orchestrator-owned writes); only 05-05-SUMMARY.md added (force-add, .planning/ ignored).