--- phase: 05-routing-route-display plan: 03 subsystem: bot tags: [kv, types-only-contract, guidance-sessions, ttl, envelope, privacy, tdd, source-introspection] # Dependency graph requires: - phase: 05-routing-route-display plan: 01 provides: the polyline6 storage format the snapshot's geometry field cites by name (routing workspace, 05-01) and the RouteResult summary fields (distanceM/durationS) the snapshot headlines - phase: 03-mini-app-shell-geolocation provides: the SessionClaim field-freeze law (bot/src/kv/sessions.ts "a 5th property means drift") and the location-pin TTL precedent (bot/src/kv/location-pin.ts) D-07 aligns with provides: - "GuidanceSessionEnvelope — the frozen Phase 6 contract: EXACTLY [id, chat_id, route_snapshot, created_at]" - "GuidanceRouteSnapshot — the INLINE route record (D-07): destinationId/Name, distanceM/durationS, polyline6 geometry, startedAtSec" - "GUIDANCE_SESSION_TTL_SECONDS = 7200 — the ~2h TTL as a documented constant awaiting its Phase 6 consumer" - "guidance: key-scheme documentation in the module header" - "GE1–GE7 — seven mechanical gates pinning the types-only boundary (field freeze, inline snapshot, TTL value, no runtime behavior, no new namespace, no Env field, no premature consumer)" affects: [phase-6 guidance turn-card service, bot privacy gates] # Tech tracking tech-stack: added: [] # zero dependencies — a types-only module patterns: - "Types-only frozen contract: interface + TTL constant + key-scheme doc, with source-introspection gates proving no runtime shadow (extends the privacy.test.mjs grep-gate family)" - "Negative-space boundary gates: guards over files that must NOT change are green by construction and redden only on drift (05-01 C4/C5/C6 precedent)" key-files: created: - bot/src/kv/guidance-sessions.ts - bot/test/guidance-envelope.test.mjs modified: [] # zero existing files touched key-decisions: - "Module header says 'No KV binding' instead of naming the KVNamespace type — the plan's own acceptance gate requires that token to appear NOWHERE in the file, comments included (grep -rc = 0); same law, gate-clean wording" - "GE5–GE7 (no new namespace / no Env field / no consumer) are RED-green-by-construction boundary guards: they scan files that must NOT change, so they pass before and after this plan and redden only on future drift — documented in the suite header per the 05-01 C4/C5/C6 precedent" patterns-established: - "Contract-freeze gate naming: GE* gates live in the consuming workspace's test dir and lazy-import the contract module so a missing module is a per-case RED, not a suite load failure" - "Phase-6 handoff note in gate doc-comments: when the service legitimately adds the binding/field/consumer, updating GE4–GE7 is THAT plan's responsibility" requirements-completed: [] # plan frontmatter requirements: [] — the GUID-05/06 upstream obligation, not a tracked requirement ID # Metrics duration: 5min completed: 2026-10-23 --- # Phase 5 Plan 3: Guidance-Session Contract Freeze (Types-Only) Summary **The `guidance_sessions` envelope frozen as a types-only Phase 6 contract — 4-field envelope + inline 6-field route snapshot (D-07: one key, one TTL) + the 7200 s TTL constant + `guidance:` key scheme, with zero runtime behavior and seven GE gates proving the boundary mechanically.** ## Performance - **Duration:** 5 min - **Started:** 2026-10-23T21:05:12Z - **Completed:** 2026-10-23T21:09:35Z - **Tasks:** 1/1 (TDD RED→GREEN) - **Files modified:** 2 (both created; zero existing files touched) ## Accomplishments - **The Phase 6 contract exists in code** (D-06): `GuidanceSessionEnvelope` (EXACTLY `id`, `chat_id`, `route_snapshot`, `created_at` — the SessionClaim field-freeze law) and `GuidanceRouteSnapshot` (EXACTLY `destinationId`, `destinationName`, `distanceM`, `durationS`, `geometry`, `startedAtSec`), the `guidance:` key scheme, and `GUIDANCE_SESSION_TTL_SECONDS = 7200` — pinned by GE1/GE2 so drift reddens the suite. - **D-07 is mechanical, not aspirational**: `route_snapshot: GuidanceRouteSnapshot` is typed INLINE (GE3), the tokens `snapshotRef`/`snapshot_ref` are banned across all of `bot/src`, and the TTL law means one key, one expiry — nothing outlives 2h, nothing is reconstructable after expiry. - **The types-only boundary cannot leak this phase** (T-05-08): GE4 comment-strips the module and bans every behavior token (KV binding type, `.put(`, `.get(`, `.delete(`, function exports, imports); GE5 pins wrangler.jsonc to EXACTLY ONE kv namespace (`chat_sessions`); GE6 pins `Env` free of any guidance field; GE7 bans any `bot/src` import of the module — no runtime shadow, no premature consumer. - **Zero existing bot files modified**: `wrangler.jsonc` and `config.ts` are untouched; the full bot suite is green at 72/72 (65 pre-existing + 7 GE gates) and `bot:typecheck` exits 0 (the module sits inside the bot tsconfig's `include: ["src"]`). ## Task Commits Each gate of the TDD cycle was committed atomically: 1. **task 1: the frozen envelope types + the types-only gate suite** - `0b8671e` test(05-03): add failing guidance-envelope types-only gates (GE1-GE7) (RED) - `d5c2bc0` feat(05-03): freeze guidance_sessions envelope as types-only contract (GREEN) **Plan metadata:** committed separately (docs, force-added — `.planning/` is gitignored) ## TDD Gate Compliance - RED gate: `0b8671e` — GE1–GE4 fail on the absent module (ERR_MODULE_NOT_FOUND / ENOENT, verified as the exact failure modes); GE5–GE7 pass as documented green-by-construction boundary guards. - GREEN gate: `d5c2bc0` — 72/72 pass, typecheck exit 0. - REFACTOR gate: not needed — 58 lines of pure types + one constant; nothing to clean. ## Files Created/Modified - `bot/src/kv/guidance-sessions.ts` — the frozen contract: module header (types-only law, D-06/D-07 citations, `guidance:` key scheme), `GUIDANCE_SESSION_TTL_SECONDS = 7200`, `GuidanceRouteSnapshot` (6 fields), `GuidanceSessionEnvelope` (4 fields). No imports, no functions, no class. - `bot/test/guidance-envelope.test.mjs` — GE1–GE7: TTL constant value; interface-body field-freeze regex (`^\s{2}\w+:` between `interface X {` and `\n}`); inline-snapshot type + repo-wide ref-token ban; comment-stripped behavior-token ban (the routing C4 `stripComments` pattern); comment-stripped JSON.parse of wrangler.jsonc; config.ts token ban; import-statement ban across the recursive `bot/src` tree. ## Decisions Made - **Header wording: "No KV binding" instead of the KVNamespace type name** — the plan's `` header template contains that literal token, but the plan's own acceptance criterion requires `grep -rc 'KvNamespace'` to return 0 with "the word never appears" (comments included). The mechanical gate wins; the law stated is identical. - **GE5–GE7 declared green-by-construction in the RED commit** — they are negative-space guards over files that must NOT change, so they cannot fail while the boundary holds (their RED state is vacuous). Mirrors 05-01's C4/C5/C6 zero-laws precedent; documented in both the suite header and the RED commit message. ## Deviations from Plan None — plan executed exactly as written. The two items above are judgment calls inside the plan's own acceptance gates, documented for the verifier. ## Known Stubs None — the module ships types + one constant by design (D-06); nothing flows to UI, nothing consumes it at runtime (GE7 pins that). Phase 6 builds the service on this frozen shape. ## Threat Surface Scan No new surface beyond the plan's ``: no endpoints, no auth paths, no file access, no schema at a trust boundary — the module has zero runtime behavior. All three register entries have implemented, gate-covered mitigations: T-05-08 (premature persistence → GE4/GE5/GE6/GE7), T-05-08b (retention → GE1 + GE3 inline snapshot), T-05-08c (contract drift → GE2 exact field sets). No threat flags. ## Issues Encountered - Pre-existing uncommitted working-tree changes to `bot/test/cari.test.mjs` and `bot/test/commands.test.mjs` (a prior session's register-commands scope-shadow fix pins) were present at start. Baseline verified green WITH them (65/65) before any of my commits; they were left untouched and excluded from every commit. Out of scope per the plan-boundary rule. - Node lives under nvm in this environment — verification commands sourced `~/.nvm/nvm.sh` first (the 05-01 precedent). No project impact. ## User Setup Required None — no external service configuration required. The module ships no behavior; nothing deploys. ## Next Phase Readiness - **Phase 6 (backgrounded turn-card service)**: consumes `GuidanceSessionEnvelope` / `GuidanceRouteSnapshot` / `GUIDANCE_SESSION_TTL_SECONDS` / the `guidance:` key scheme verbatim — the shape cannot have drifted (GE1/GE2 pin it). Phase 6's plan owns: the KV namespace binding + Env field (relaxing GE5/GE6), the consumer import (relaxing GE7), and extending the PG1 same-line-`expirationTtl` grep gate to `guidance:` writes. - **Phase 5 remaining plans**: none depend on this module (depends_on: [], wave: 1); 05-04/05-05 web-side work is unaffected. --- *Phase: 05-routing-route-display* *Completed: 2026-10-23* ## Self-Check: PASSED - All 2 created files verified present on disk (guidance-sessions.ts, guidance-envelope.test.mjs). - Both plan commits verified in git log (0b8671e RED, d5c2bc0 GREEN). - `npm test -w bot`: 72 passed, 0 failed (PASS GE1…GE7 + all 65 pre-existing cases). `npm run bot:typecheck`: exit 0. - All seven acceptance greps pass on the module (envelope iface, snapshot iface, TTL const, key scheme, KvNamespace count = 0, inline snapshot). - Zero existing files modified by this plan; STATE.md and ROADMAP.md untouched (orchestrator-owned writes).