- 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
- 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
- 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
- BA9: summed gz size of fixtures dataset + routing/src + web/src/route
sources <= 204800 bytes, measurement printed every run (D-11, T-05-12)
- BA10: every committed pairId must appear in the shipped JS assets
(the BA7 analog — a stale fixture-less bundle is unshippable)
- BA5: ROUTE_PROFILE_HARNESS_ONLY joins the harness-leak token scan
(the 05-05 route-profiles module must be dead-code-eliminated from dist)
- TDD note: gates target artifacts already shipped by 05-02/05-04, so no
failing RED run exists; mechanism teeth proven (bogus pairId absent)
- BA7: the shipped JS must carry EVERY committed gazetteer id (the
web half of bot/web id-set parity, T-04-39) — RED against the
pre-gazetteer bundle, green after the resolver lands in the build
- BA8: the shipped JS carries NO deep-link crypto (JS assets only —
the official Telegram SDK is external, D-11/T-04-40)
- no-storage: explicit web/src/search memory-only + no-crypto pin
- capture.mjs: the ARAHARAH-DEST signal race + the 'dest' wait mode
(state=resolved asserted, then the rendered card's text is waited
on — the card mounts at live, T-04-42) + the shell-dest-preload
leg; 8/8 expected captures
- homescreen.ts: wireHomeScreen behind the BOTH-halves Bot API 8.0
guard; 'added' hides (no nag), 'unknown' (always iOS) shows the
neutral prompt — indeterminate, never an error; homeScreenAdded
hides; a refused addToHomeScreen is caught, the affordance stays;
.btn-secondary in the existing strip — no new furniture; NO
beforeinstallprompt/manifest/service worker (T-04-38)
- main.ts: locale derivation moved above the bindings block; stage 13
reads BOTH destination carriers, seeds the memory-only holder, builds
the destination/unavailable surface (null when no id — Phase 3 boot
byte-identical, D-12); the live register shows the destination
surface; the ARAHARAH-DEST state-token signal fires unconditionally
- index.html: ?dest=<id> harness profile threaded to the mock's
startParam; the block stays inside __ARAHARAH_HARNESS__
- HS1-HS9 green (54/54), web:typecheck green
Capability guard (both halves), the four status branches ('added'
hides, 'unknown' shows with the neutral prompt, 'missed' shows,
'unsupported' nothing), tap → addToHomeScreen, homeScreenAdded →
hidden, and the browser-install-API ban. Suite load fails until
web/src/telegram/homescreen.ts exists (RED verified: 45 passed,
1 failed suite-load).
- resolve-destination.ts: reads ?dest= (URL, primary — the inline
web_app-button carrier) then initData's start_param (the signed
string, secondary — the direct-link/home-screen carrier), ONE
slug validation, dest_ prefix stripped; resolves against the
webview's own build-time @araharah/search copy; honest null for
unknown ids; module-scope memory-only holder (GEO-03/D-07)
- destination-card.ts: the confirmation + honest-unavailable cards,
createElement/textContent only, existing theme classes (T-04-32)
- webapp.ts: TelegramWebAppLike gains startParam, addToHomeScreen?,
checkHomeScreenStatus? + HomeScreenStatus (Bot API 8.0+ members)
- telegram-mock.ts: satisfies the extended contract in the same
change (startParam initData wiring, homeScreenStatus, __homeScreenAdded)
- strings.ts: 5 new copy fields in en+ms; web depends on @araharah/search
- DP1-DP12 green (45/45 web suite), web:typecheck green
Two carriers, one validation, memory-only resolution against the
webview's own build-time gazetteer copy. Suite load fails until
web/src/search/resolve-destination.ts exists (RED verified: 33 passed,
1 failed suite-load).
- theme.ts: colorScheme → data-tg-theme on <html>; header_bg_color 0-1 luminance fallback
(T-03-11 — Telegram hex is a signal, never a consumed CSS value); applyTheme's ONLY
statement is the attribute set (Pitfall 7 / D-10, greppable invariant)
- backbutton.ts: hide() at init (one shell state), onClick is total — close() at the root,
no dead-ends (UI-SPEC §BackButton)
- safe-area.ts: env(safe-area-inset-*) probe ∪ WebApp.safeAreaInset, per-edge max,
version-gated at 8.0, applied as --safe-* custom properties on :root
- webapp.ts + backbutton/safe-area never read node_modules types;
@types/telegram-web-app stays dropped (web/package.json untouched)