Files

132 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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:<id> 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:<id>` 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:<id>` 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:<id>` 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 `<interfaces>` 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 `<threat_model>`: 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:<id>` 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).