Files
arah2/.planning/phases/05-routing-route-display/05-05-SUMMARY.md
T
araharah-executor efba4d6f4c 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)
2026-10-24 06:04:52 +08:00

16 KiB
Raw Blame History

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
05-routing-route-display 05 verification-gates
bundle-budget
build-artifact-gates
capture-harness
fixpair
state-tokens
privacy-gates
worker-boundary
playwright
tdd
phase provides
05-routing-route-display/01 getPairOrigin (the pinned-public-origin lookup the ?fixpair knob resolves through) and the routing workspace sources BA9 measures
phase provides
05-routing-route-display/02 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 provides
05-routing-route-display/04 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
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=<pair-id> 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)
phase-6-guidance (consumes the painted-route state machine + capture discipline)
phase-verifier (SC3 evidence chain)
future BA budget growth
added 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
created modified
web/src/mock/route-profiles.ts
bot/test/route-privacy.test.mjs
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)
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
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
RTNG-01
RTNG-02
19min 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=<pair-id> 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 <threat_model>: 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).