Files
arah2/.planning/phases/06-turn-by-turn-guidance-live-driving/06-03-SUMMARY.md
T

15 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
06-turn-by-turn-guidance-live-driving 03 bot
kv
ttl-only
guidance-sessions
envelope
api-endpoints
bearer-claim
privacy
gate-repin
tdd
workspace-dep
phase plan provides
05-routing-route-display 03 the FROZEN GuidanceSessionEnvelope/GuidanceRouteSnapshot shape + GUIDANCE_SESSION_TTL_SECONDS + guidance:<id> key scheme (this plan implements, never redefines)
phase plan provides
05-routing-route-display 01 decodePolyline6 — the pure codec validating POST body geometry server-side
phase plan provides
03-mini-app-shell-geolocation 01 the Bearer-claim precedent (handleLocation) and getClaimByToken — the only identity source for /api/guidance
mintGuidanceSession/getGuidanceSession/deleteGuidanceSession — the TTL-only envelope service over the unchanged frozen shape (GE1/GE2/GE3 hold)
POST/GET/DELETE /api/guidance — chat identity from the Bearer claim ONLY; body chat_id structurally ignored (T-06-02); cross-chat read/delete impossible (T-06-02b)
MAX_GUIDANCE_BODY_CHARS = 8192 with the WR-01 pre/post-read double check
RP2 re-pinned as an explicit allowlist: exactly @araharah/routing/src/polyline.ts may enter the Worker
PG1 storageModules now covers kv/guidance-sessions.ts (same-line TTL law on the new write path)
phase-6 guidance turn-card cron (plan 07 reads envelopes)
GUID-05 web POST-on-minimize
GUID-06 resume
GUID-06 trip-end DELETE
added patterns
@araharah/routing:* in bot/package.json (workspace dep; only the pure polyline6 codec subpath is imported)
Types-frozen + service-beneath: behavior added BELOW an interface whose bytes never changed (GE2 runtime twin = exact-key checks in the defensive read)
Gate re-pins ship with the invalidating change: GE4/GE7 (service live / consumers scoped) in 0cfa0bd, RP2 (codec-only allowlist) in 7875ed4
Resolve-then-OWN endpoint posture: getGuidanceSession → envelope.chat_id === claim.chat_id, else an indistinguishable 404 (no existence oracle)
created modified
bot/src/kv/guidance-sessions.ts
bot/src/webhook.ts
bot/package.json
package-lock.json
bot/test/guidance-envelope.test.mjs
bot/test/webhook.test.mjs
bot/test/privacy.test.mjs
bot/test/route-privacy.test.mjs
RP2 re-pin is an ALLOWLIST, not a lifted ban: every @araharah/routing specifier in bot/src must be EXACTLY the pure codec subpath — the routing stack (graph/A*/fixtures) stays web-side; only charset/decode validation crosses the boundary
The envelope service REUSES the chat_sessions namespace under guidance:<id> keys — no second kv_namespaces entry (GE5 stands), no new Env field (GE6 stands); the binding is handed in structurally (the sessions.ts/location-pin.ts KvLike seam)
validateSnapshot builds the snapshot field-by-field, so a body chat_id (or any extra property) is structurally dropped before mint — T-06-02 by construction, proven by the GW4 injection probe
404 for absent, malformed, AND foreign ids — indistinguishable by design (T-06-02b, no existence oracle); GW7/GW10 additionally prove a foreign GET/DELETE never deletes
Guidance endpoint test block (GW1–GW11): claims minted through the REAL /api/session path (mintSession) against the Map-fake KV, write-count assertions made relative to the claim-mint baseline
GUID-05
~8min (commits 05:02:28Z–05:06:25Z; resumed once — see Deviations) 2026-10-24

Phase 6 Plan 3: Guidance Envelope Service + /api/guidance Endpoints Summary

The Phase 5-frozen GuidanceSessionEnvelope goes LIVE (D-11's precondition): a TTL-only mint/get/delete service beneath the byte-identical 4-field shape, plus Bearer-claim-gated POST/GET/DELETE /api/guidance — chat identity from the claim only, every write TTL'd at 7200 s, corrupt records read as null, and the RP2 routing ban re-pinned to a codec-only allowlist.

Performance

  • Duration: ~8 min (task commits 05:02:28Z → 05:06:25Z; session resumed once — see Deviations)
  • Started: 2026-10-24T05:02:28Z (first task commit)
  • Completed: 2026-10-24T05:06:25Z (last task commit); continuation verification + summary ~05:08Z
  • Tasks: 2/2 (both TDD RED→GREEN)
  • Files modified: 8 (zero created — the plan's whole point was to add behavior inside existing modules)
  • Suites: bot 74 → 90 tests (5 GS behavioral + 11 GW endpoint), 90/90 green; bot:typecheck exit 0

Accomplishments

  • The envelope service exists under the frozen shape (task 1): mintGuidanceSession (crypto.randomUUID id, claim-derived chat_id, ONE put at guidance:<id> with same-line expirationTtl: GUIDANCE_SESSION_TTL_SECONDS), getGuidanceSession (UUID-shape guard BEFORE any KV touch — V5; try/catch parse; exact-key 4+6 field check + per-field type/finite/range validation mirroring the location-pin defensive read), deleteGuidanceSession (active trip-end sweep, D-14's server half; malformed id is a silent no-op). The envelope/snapshot interfaces are byte-identical to Phase 5 — GE1/GE2/GE3 pass unchanged.
  • The boundary gates re-pinned WITH the change (0cfa0bd): GE4 flipped from the types-only behavior-token ban to "the service trio is exported"; GE7 flipped from a total consumer ban to "named imports of the service surface only" (mint/get/delete, the TTL constant, the two types — no default/namespace/side-effect end-run). GE5 (exactly ONE kv namespace) and GE6 (no Env guidance field) stand untouched because the service reuses chat_sessions.
  • /api/guidance is live over HTTP (task 2): POST (401 → 413 → 400 → 201 classes; hand-rolled snapshot validation — slug destinationId per the search workspace's ENTRY_ID_PATTERN law, bounded destinationName, finite non-negative metrics, safe-integer startedAtSec, geometry that decodePolyline6 decodes to ≥ 2 points) → mintGuidanceSession(env.chat_sessions, claim.chat_id, snapshot) → 201 {id}. GET returns the envelope only to the OWNING chat; DELETE removes the record actively. Non-POST/GET/DELETE → 405.
  • The threat register's mitigations are all code, all gate-proven: T-06-02 (body chat_id: 999 with claim chat 101 stores 101 — GW4 injection probe; snapshot built field-by-field so extras are structurally dropped), T-06-02b (foreign/absent → identical 404 {error:"not_found"}; the record SURVIVES a foreign GET/DELETE — GW7/GW10), T-06-04 (every log line is a literal category — [guidance] rejected: unauthorized|malformed; no ids, no coordinates, no payloads; TTL 7200 s on every write — PG1+GE4+GS1), DoS (MAX_GUIDANCE_BODY_CHARS 8192 with the pre/post-read double check — GW3 proves the lying-Content-Length backstop).
  • PG1 grew with the write path: kv/guidance-sessions.ts joined storageModules in the same commit that added the .put( (the PATTERNS caution honored mechanically).

Task Commits

  1. task 1: envelope service + GE4–GE7 re-pins + PG1 (TDD)
    • 0cfa0bd test(06-03): re-pin GE4/GE7 to the live service + behavioral envelope suite + PG1 storage list (RED)
    • c87dc7d feat(06-03): guidance envelope service — TTL-only mint/get/delete beneath the frozen shape (GREEN)
  2. task 2: /api/guidance POST + GET + DELETE (TDD)
    • 212aa57 test(06-03): guidance endpoint suite GW1-GW11 — 401/413/400 classes, claim-owned chat (T-06-02 injection probe), own-vs-foreign GET/DELETE, 405 (RED)
    • 7875ed4 feat(06-03): /api/guidance POST/GET/DELETE — Bearer-claim gated, TTL-only service beneath (GREEN) — includes the RP2 re-pin + @araharah/routing workspace dep

TDD Gate Compliance

  • Task 1: RED 0cfa0bd (GE4/GS1–GS5 fail on the absent service; GE1/GE2/GE3 pass unchanged; GE5–GE7 green-by-construction boundary guards, documented) → GREEN c87dc7d (90-suite predecessor green).
  • Task 2: RED 212aa57 (GW1–GW7, GW9–GW11 fail on the missing route arm; GW8 green-by-construction — the catch-all already 404s; noted in the RED commit message) → GREEN 7875ed4.
  • Both RED→GREEN pairs verified in git log; no gate skipped.

Files Created/Modified

  • bot/src/kv/guidance-sessions.ts — frozen types untouched; the service added below them: GuidanceSessionKv seam, ENVELOPE_ID_PATTERN + exact-key tables (ENVELOPE_KEYS/SNAPSHOT_KEYS), mint/get/delete with the defensive-read field law (isEnvelopeValid). Module header now states the live-service law: shape frozen (GE2); the service owns every write; every write carries expirationTtl: GUIDANCE_SESSION_TTL_SECONDS.
  • bot/src/webhook.ts — /api/guidance route arm + handleGuidance (method gate → Bearer claim → POST cap/validate/mint | GET/DELETE resolve-then-own), MAX_GUIDANCE_BODY_CHARS = 8192, DESTINATION_ID_PATTERN (mirrors search/src/source.ts), MAX_DESTINATION_NAME_CHARS = 200, validateSnapshot (field-by-field construction), category-only response helpers; header route table documents the new surface.
  • bot/test/guidance-envelope.test.mjs — GE1–GE3 unchanged in meaning; GE4/GE7 re-pinned; GS1–GS5 behavioral (TTL'd mint, round-trip, 7-class defensive null read, UUID key-guard with a KV-untouched counter, active delete).
  • bot/test/webhook.test.mjs — GW1–GW11 offline undici block; claims minted through the real /api/session path; injection probe; log-literal assertions.
  • bot/test/privacy.test.mjs — PG1 storageModules + kv/guidance-sessions.ts.
  • bot/test/route-privacy.test.mjs — RP2 re-pinned to the codec-only allowlist (header documents the 06-03 re-pin and why); RP1 unchanged.
  • bot/package.json + root package-lock.json — "@araharah/routing": "*" workspace dep.

Decisions Made

  • RP2 as an allowlist, not a lifted ban — the plan required import { decodePolyline6 } from "@araharah/routing/src/polyline.ts" in webhook.ts, which the old RP2 (zero @araharah/routing tokens in bot/src) forbids. Per the gate re-pin law (PATTERNS: "the plan adding the routing import re-pins RP2, listed explicitly"), the GREEN commit narrows RP2 to: every routing specifier in bot/src must be EXACTLY the pure codec subpath. The routing stack (router/graph/A*/fixtures) still cannot enter the Worker; RP1 (no /api/route proxy) untouched.
  • Envelope storage reuses chat_sessions — the frozen key scheme (guidance:<id>) lives in the existing namespace, so GE5/GE6 stay green without edits; no second namespace was created (R2 later is a different binding class).
  • deleteGuidanceSession UUID-guards like the read — a malformed id constructs no KV key at all (silent no-op), closing arbitrary-key deletion symmetrically with the V5 read guard.

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] RP2 gate re-pin (file not in files_modified)

  • Found during: task 2
  • Issue: the plan's required codec import in bot/src/webhook.ts makes the existing RP2 gate (zero @araharah/routing specifiers in bot/src) permanently red — task 2's own verify (npm test -w bot green) is impossible without touching bot/test/route-privacy.test.mjs, which the plan's file list omitted.
  • Fix: re-pinned RP2 to the codec-only allowlist in the same GREEN commit as the import, per the repo's explicit gate-re-pin law (PATTERNS "Gate re-pins ship WITH the change that invalidates them", which names RP2 and THIS kind of plan as the owner).
  • Files modified: bot/test/route-privacy.test.mjs
  • Commit: 7875ed4

2. [Rule 1 - Bug] GW test write-count baselines corrected at GREEN

  • Found during: task 2 GREEN
  • Issue: the RED fixtures minted claims via the real mintSession (2 KV writes) but several GW cases asserted absolute ttlLog.length (0 or 1) — a fixture-counting bug that would have made honest implementations fail.
  • Fix: counts made relative to a writesBefore baseline captured after the claim mint; assertion strength unchanged (the guidance write is still proven singular, TTL'd, and correctly keyed).
  • Files modified: bot/test/webhook.test.mjs
  • Commit: 7875ed4

Session Continuation Note

This plan executed across two executor sessions: the first landed all four task commits but its final working-tree writes flushed late (observed by the continuation agent as a tree that settled between checks). The continuation agent verified every gate post-flush (all four commits present, 90/90 green, typecheck clean, no untracked residue, no deletions in any plan commit) rather than re-executing. No work was redone or lost.

Known Stubs

None — the service and all three endpoints are fully wired to the real KV binding (env.chat_sessions); nothing is mocked, placeholdered, or deferred. Plan 07's cron is the designed next consumer, not a missing piece of this plan.

Threat Surface Scan

No surface beyond the plan's <threat_model>: one new endpoint family (/api/guidance, Bearer-gated — covered), one new KV write path (TTL-only — covered by PG1/GE4), no new bindings, no file access, no schema at a trust boundary. All four STRIDE mitigations are implemented and gate-proven (see Accomplishments). No threat flags.

Issues Encountered

  • Node lives under nvm in this environment — verification commands source ~/.nvm/nvm.sh first (the 05-01/05-03 precedent). No project impact.
  • STATE.md / ROADMAP.md / REQUIREMENTS.md intentionally untouched — orchestrator-owned writes after the wave completes (GUID-05 completion is recorded above for the orchestrator to tick).

User Setup Required

None — no new secrets, no new wrangler bindings, no external services. The workspace dep links locally via npm workspaces; deploy needs nothing beyond the existing chat_sessions namespace.

Next Phase Readiness

  • Plan 07 (cron turn-cards): reads envelopes through getGuidanceSession's defensive validation (corrupt → null, never pushed); iterates guidance: keys; pushes to envelope.chat_id only.
  • Web plans (GUID-05/06 flows): POST on minimize (envelope in → {id} out), DELETE on trip end (D-14's client half already has its server twin), GET on resume.
  • Verifier hooks: GE1–GE7, PG1, RP1/RP2, GW1–GW11, GS1–GS5 all pin the surface mechanically; the injection probe (GW4) is the regression canary for the claim-only identity law.

Phase: 06-turn-by-turn-guidance-live-driving Completed: 2026-10-24

Self-Check: PASSED

  • All 8 modified files + the SUMMARY verified present on disk.
  • All four plan commits verified in git log (0cfa0bd RED, c87dc7d GREEN, 212aa57 RED, 7875ed4 GREEN) — correct RED→GREEN interleaving per task.
  • npm test -w bot: 90 passed, 0 failed (GE1–GE7 re-pinned + GS1–GS5 + GW1–GW11 + all 74 pre-existing). npm run bot:typecheck: exit 0.
  • Acceptance greps re-run post-flush: expirationTtl: GUIDANCE_SESSION_TTL_SECONDS ×3 in the service module; MAX_GUIDANCE_BODY_CHARS = 8192 at webhook.ts:225; all [guidance] log lines are literal categories (no interpolation).
  • No deletions in any plan commit; no untracked non-planning files; STATE.md / ROADMAP.md / REQUIREMENTS.md untouched.