- GC1-GC7: PURE projectTurnCards behavior — projection math, cadence table,
dead-trip gate, 4-line caption anatomy, street-bearing DEFAULT (D-12) vs
bare-verb degradation, real makeStreetAt against the committed mini-graph
- GC8: the pure-module structural law (no console, routing-workspace imports,
nameId street hop, zero KV writes)
- CS7 in commands.test.mjs pins the 21 UI-SPEC turn-card keys in en + ms
- route.ts: onDeactivated POSTs the FROZEN six-field snapshot with
keepalive (the minimize race's belt) and stores the server-minted id;
onActivated DELETEs (the cron pauses — structural) and RETAINS the id;
teardown's deleteEnvelope completes the idempotent pair; a live
envelope is never double-minted; best-effort honesty throughout (D-13)
- resume(trip): the controller computes the projection from
startedAtSec + avg speed (the cron's own math, one formula), renders
GUIDE // RESUMING + gantry + projected card, draws the rebuilt line,
arms the watch, DELETEs immediately (a resumed boot is foregrounded),
and seeds the session mid-route on the first fix through the machine's
own resuming path — synthesized terminal marks keep the arrive mark at
the line's END (a steps-less envelope model would instantly arrive)
- backAction(): the PX-01 resolver's single authority — driving →
confirm, confirm → CONTINUE, ended/route → the walk, root → close
- guidance.ts: resumingView keeps the progress math in guidance/ (RS6)
- main.ts: the COMPOSED LADDER (engine-a first, fixture fallback —
code-constructed, zero env reads), the ?g= UUID-validated reader with
the defensive envelope validation (fail-honest → honest boot), the
capability-guarded activated/deactivated wiring, the recenter
re-lock at the guidance pose, the chain-fix camera skip while driving,
and the no-double-consult resume guard
- merged suite: G9-G12 + the E-family (co-executor tests reconciled:
pre-gate probe, builder-format asserts, terminal-mark fixture)
— 146 passed, typecheck clean
- G9 minimize POST: EXACTLY the frozen six-field snapshot (id/chat_id/
created_at are server-minted), keepalive:true (the minimize race),
the Bearer header; a live envelope is never double-minted
- G10 DELETE with the stored id on activated (the cron pauses —
structural) AND at teardown (the D-14 active sweep, idempotent)
- G11 ?g= resume: GUIDE // RESUMING + gantry + projected numbers, the
watch armed, the line drawn; the first fix flips live ON_ROUTE through
the machine's own resuming path; the ?g= envelope deletes at teardown
- G12 main.ts composition gate: createHttpRoutingSource + the
'?? (await' ladder, the UUID-validated ?g= carrier, the
activated/deactivated wiring, the guidance-aware camera pose, the
no-double-consult guard, zero env reads
- route.ts grows the pump beside the Phase 5 graph: the injected watch arms
with the route draw; the first fix opens the 06-02 session; the start
gate (>= GUIDANCE_START_ALONG_M along) activates the cockpit — PX-03
close protection, [data-guidance=active], banner + card + list, camera
eased to the SNAPPED line, state=on_route token
- effects dispatch: haptics via fireHaptic, reroute-requested through the
injected RoutingSource (fail-honest catch; success = setData snap-in with
NO camera jump + list re-render; failure = calm NO REROUTE + card state
line); arrival -> the D-14 teardown (watch stop, protection release,
chrome clear, trip-end card center)
- END TRIP/BackButton -> the ONE confirm card; YES ABANDON = same teardown;
CONTINUE dismisses with guidance alive; guidancePhase exposes
none/driving/confirm/ended for the PX-01 resolver; walkBack refuses
while the confirm owns the exit and vacates the route stack on the walk
- guidanceView (guidance.ts): banner/card/list view data composed beside
the machine's own maneuver marks — route/ imports and calls it, never
re-derives (the RS6 re-pin's boundary; zero banned tokens in route/)
- G1-G8 green; RS6/RS8 re-pins green; 137 passed, typecheck clean
- G1 activation gate: sub-threshold fix keeps the Phase 5 route state;
a fix >= 30 m along mounts the cockpit (data-guidance, close protection,
banner + card, the locked camera pose, state=on_route token)
- G2 pump updates: banner distance/street from the model, ETA card,
stepping progress bar (pct = alongM/totalM)
- G3 no premature signal during the OFF_ROUTE grace; G4 REROUTING calm
swap + dash + gantry with exactly ONE source.route call under the storm
guard; G5 pending precondition + NO REROUTE fail-honest degradation;
G6 reroute success = setData snap-in, NO camera jump, gantry stop
- G7 END TRIP -> confirm -> YES ABANDON teardown (watch stop, protection
release, trip-end card, state=ended); CONTINUE dismisses, guidance alive
- G8 arrival runs the same teardown from the machine's arrival effect
- RS6 re-pinned: banned tokens only on ../guidance/ import lines (guidance
IMPLEMENTATION lives in web/src/guidance/); RS8 re-pinned: the
ARAHARAH-GUIDE token family joins the console discipline
- guidance/camera.ts: GUIDANCE_CAMERA_PADDING {320,64,64,64} / ZOOM 16 /
EASE_MS 400 / PITCH 35; easeToSnapped rides the follow controller (ONE
easing discipline) and never passes bearing — north-up, recorded call;
onRerouteApplied is the documented camera no-op (setData lives in the
route layer)
- map/follow.ts: follow(point, opts?) pose overrides spread over the
Phase 3/5 defaults — no-opts calls stay byte-identical (RS13); the
re-lock announces the mode (the recenter notch's return signal, RS12);
the originalEvent unlock law untouched
- telegram/backbutton.ts: BackAction resolver ({hidden|close|callback})
consulted per click + wire.sync() visibility re-apply (visible iff
actionable); the legacy (hasOverlay, walkBack) shape composes internally
so Phase 5 call sites and RS7 stay valid; close() only at the true root
- RS7b: the context-aware BackButton resolver walks search → routes →
guidance → (confirm) → routes → search; close() NEVER fires from a
pushed state; the wire exposes sync() for state changes
- RS11: GUIDANCE_CAMERA_PADDING/{ZOOM,EASE_MS,CAMERA_PITCH} pinned at the
UI-SPEC pose; bearing key NEVER present; onRerouteApplied moves nothing
- RS12: programmatic moves never unlock follow; a user movestart
(originalEvent) unlocks — Phase 3 free state returns
- RS13: follow(fix) without opts stays byte-identical (no pitch/zoom keys)
- fakeMap grows flyTo/on/emit recording; suite header documents the RS6/RS8
re-pins landing with the guidance wiring (tasks 2-3)
- buildGuidanceCard: GUIDE register, verbatim headline, live telemetry row (MIN / {km} KM / ETA), 4px progressbar with full aria value set + instant inline width, no-reroute state line only when degrading, ONE btn-secondary END TRIP
- buildConfirmCard: GUIDE // CONFIRM + exact en/ms privacy copy, btn-primary YES ABANDON + btn-secondary CONTINUE, guide-confirm bottom-register hook — the ONE primary CTA in guidance chrome
- buildTripEndCard: GUIDE // END, exact deletion copy, no actions
- shell.css guidance block: banner chassis + 1px separator, CSS-only strip compaction under [data-guidance], list panel + row states + lamp, progress bar — token vars only, zero motion/depth/radius/hex (theme audit clean)
- 8 hand-authored 32x32 SVGs per the UI-SPEC geometry table (3px stroke, square caps, orthogonal/45° only; uturn = machined staple, no arc)
- glyphs.ts: GLYPH_KINDS/GLYPHS via Vite ?raw (committed files are the single source) + svgHost — the ONE sanctioned innerHTML site, a static import value
- strings.ts: 18 ShellStrings keys appended in en + ms, values verbatim from the 06-UI-SPEC copywriting contract
- copy-gate: COPY1 floor 40→58 + COPY8 pins all 18 guidance keys verbatim in both locales
- upload-road-graph.mjs: wrangler-shelled maintainer tool (pre-flight 1-80MB,
round-trip verify hint); bucket araharah-graphs NOT created — R2 is not yet
enabled on the account (wrangler code 10042) — dashboard one-time activation
is the phase's user_setup item
- measure-route-cpu.mjs: 25-pair gate (corpus + seeded quadrant spread),
hrtime around routeAstar, warm-up excluded, unreachable probes reported
SEPARATELY from the routed p95, exits 1 over the 8ms budget
- Rule 1 (measured): the class set lacked *_link ramps — the graph FRAGMENTED
(7.7% of nodes in severed motorway islands; corpus pairs 'unreachable').
5 link classes added -> 311325 nodes / 677068 edges / 14.7 MB, all landmarks
in one component; enforced floor corrected 1KB -> 1MB (D-01)
- ladder (a) drop tertiary+residential: p95 17.8ms but 12/25 pairs unreachable
— REJECTED on correctness (D-03), reverted, ladder history recorded
- ladder (b) bidirectional A* (BH, Ikeda potentials, reverse-CSR WeakMap):
provably optimal (A1 625-pair sweep green), alternation balanced by heap
size (raw-top comparison starves the backward side 19501:3), reduction
signs in the standard feasible form. Measured: p95 ~38-93ms — NO GATE PASS.
The potential split halves each side's steering, cancelling the halved radius
- GATE FAIL is the honest verdict: ladder (c) — Workers Paid vs layout-v2 ALT
(still $0) vs free-plan-with-degradation — requires maintainer sign-off;
docs/ROAD-GRAPH.md carries the decision table and the fork contract
- bot/src/routing/worker-router.ts: module-scope per-env graph memo (1 R2 GET per
cold isolate, shared with the 06-07 cron arm via loadGraph), snap+A*+deriveSteps,
claim-gated 401 / 4096-cap 413 / V5 bbox 400, 200-null on every miss class,
ONE best route (no alternatives), category-only logs
- webhook.ts: /api/route arm delegating to handleRoute (405 non-POST)
- config.ts: ROAD_GRAPH: R2Bucket — first R2 binding (never a KV namespace, GE5)
- wrangler.jsonc: r2_buckets ROAD_GRAPH -> araharah-graphs with the fork law comment
- routing/src/http-source.ts: createHttpRoutingSource — slug-guard-first, fail-honest
(throw/non-200/malformed/null-body -> null), defensive RouteResult validation;
exported from index.ts; ladder note: backend #2 LIVE
- A7 re-pinned (06-01 named 06-04 as the owner of createHttpRoutingSource)
- test-side fix during GREEN: the R2 fake hands decodeGraph an exact ArrayBuffer
(Node Buffers are views; encodeGraph output is already an ArrayBuffer)
- bot 103 passed / routing 65 passed; both typechecks clean
- POST: WR-01 cap (MAX_GUIDANCE_BODY_CHARS 8192, pre/post-read double check),
hand-rolled snapshot validation (slug destinationId, bounded name, finite
metrics, integer startedAtSec, polyline6 geometry >= 2 points via
decodePolyline6) -> mintGuidanceSession with the CLAIM's chat_id — a body
chat_id is structurally ignored (T-06-02 injection probe GW4)
- GET/DELETE: resolve then OWN — envelope.chat_id === claim.chat_id else 404,
foreign and absent indistinguishable (T-06-02b); DELETE actively removes
the record (D-14's server half); non-POST/GET/DELETE -> 405
- category-only logging: [guidance] rejected: unauthorized|malformed —
literal strings, no interpolation of body/claim values (D-14)
- RP2 re-pin ships WITH the change (gate re-pin law): the total
@araharah/routing ban becomes an explicit allowlist — exactly the
polyline6 codec may enter the Worker (geometry validation); the routing
stack stays web-side; RP1 unchanged
- bot/package.json + lock: @araharah/routing workspace dep
- offline undici Request/Response pattern; claims minted through the real
/api/session path (mintSession) against the Map-fake KV
- GW8 green-by-construction (the catch-all already 404s absent ids);
ownership teeth live in GW7/GW10
- mintGuidanceSession: crypto.randomUUID id, claim-derived chat_id arg,
ONE put at guidance:<id> with same-line expirationTtl (GE1/PG1)
- getGuidanceSession: UUID-shape guard BEFORE any KV touch (V5), exact
4+6-field + type + finite/range validation (the location-pin defensive
read law) — corrupt records read as null, never reach the cron
- deleteGuidanceSession: active trip-end sweep (D-14's server half)
- module header: the types-only note becomes the shape-frozen + service-
owns-every-write law; envelope/snapshot interfaces byte-identical (GE2)
- no new namespace (GE5), no new Env field (GE6) — the chat_sessions
binding is handed in structurally
- GE1/GE2/GE3 UNCHANGED in meaning (TTL 7200, 4+6 field freeze, inline snapshot)
- GE4 re-pinned: types-only token ban -> the module must EXPORT mint/get/deleteGuidanceSession
- GE7 re-pinned: total consumer ban -> named-imports-only of the service surface
- GE5/GE6 stand (exactly one kv namespace; no Env guidance field)
- GS1-GS5 behavioral: TTL'd mint, roundtrip, 7-class defensive null read,
UUID key-guard without KV touch (V5), active delete (D-14 server half)
- PG1 storageModules now covers kv/guidance-sessions.ts (same-line TTL law)
- shouldAcceptFix: accept when first fix, >=10 m moved, or >=5 s elapsed
(whichever first) — the pure accept rule over haversineM
- createGuidanceWatch(geo, onFix): high-accuracy watchPosition
(maximumAge: 0), six-field Fix normalization (null accuracy/heading,
source webview), rejected fixes die in the wrapper, idempotent stop()
clearing the watcher id (T-06-05b's privacy-minimal trip end)
- replay suite T1-T3 + R12b: truth table, recorded-options structural
gate, feed-only-accepted proof, stop idempotence, module purity scan
- snapToGraph: bbox+margin guard first (honest null = router-pending,
D-03), then 3x3 coarse-grid neighborhood (0.002 deg cells, WeakMap
memo per RoadGraph — LAW 1 lazy memoize, O(local) not O(n))
- projection in cos(lat)-scaled degree space (Pitfall 8); returned
distance is haversineM; result carries the best segment's nameId
(null for the unnamed sentinel) for plan 07's cron street-hop
- oneway-reverse edges stay snap-visible (direction is A*'s law);
gridBuildCount() instrumentation kept internal to the workspace
- Pinned-layout gates: round-trip, bad magic/version/truncated -> null,
zero-copy view mutation, name pool, committed mini-graph fixture pin
- Each case lazy-imports ../src/graph.ts — ERR_MODULE_NOT_FOUND is the
red state (fixture-source.test.mjs pattern)
- RP1: zero /api/route + registerRoute/routeProxy tokens in comment-stripped
bot/src — the Worker gains no route endpoint, even accidentally (T-05-16)
- RP2: zero @araharah/routing specifiers in bot/src — routing lives in the
webview bundle only; the Worker imports only @araharah/search
- scans grow with the bot/src tree automatically (the PG-gate pattern)
- TDD note: gates target an already-clean tree, so the RED run is a teeth
proof — a planted violating file fails both gates; removed, both pass
(74/0 bot suite green)
- route-profiles.ts: HARNESS-ONLY module — sentinel ROUTE_PROFILE_HARNESS_ONLY
+ getRouteProfileOrigin wrapping getPairOrigin; D-12 header (pinned PUBLIC
origin only, no coordinate params) + BA5 exclusion note
- index.html: ?fixpair=<pair-id> knob resolves the pair's pinned public
origin as the mock's telegramFix (accuracy 12, heading null); unknown
pairId keeps the default fix; knob documented in the profile comment
- [Rule 1 - Bug] main.ts never passed bootMap's opts.onLoad — route sources
never existed and setRouteDrawn's setData silently no-op'd (cards rendered,
the line never painted; invisible to the RS fakes). Wiring added + RS2
boot-wiring gate pins it
- [Rule 1 - Bug] consult raced the map load: harness session stubs resolve
the location chain before WebGL/style init, so draw landed on missing
sources and the post-load empty install discarded it (probe: ROUTE token
fired before RAMS-BASEMAP-READY). Consult now awaits mapLayersReady,
resolved in the load handler after installRouteLayers (never wedges —
finally-resolved; cards-only boots resolve immediately)
- Capture run: CAPTURE OK — 13/13; route line verified painted, alt swap
renders ROUTE 2/2 +1 MIN VS ROUTE 1, pending card honest, dark PX-02
- npm test -w web: 76 passed (new RS2 boot-wiring gate included)
- SIGNALS + seen-map + diagnostics gain ARAHARAH-ROUTE; waitMode 'route'
races the state token, asserts expectState + expectText DOM text, and
applies the PX-02 theme-slot honesty check on themed legs
- captureLeg gains an interact(page) pre-shot hook (the alt leg's chip tap)
- five legs data-driven from the committed corpus (demoPair picked at
runtime — never a hardcoded pair literal): fixture / alt / pending /
dark / route-gallery-compare (composer refactored into composeCompare)
- expected list grows 8 → 13
- RED by construction: the ?fixpair knob does not exist yet, so the
default fix sits outside the demo pair's 200m tolerance → the drawn
legs must fail with state=pending