From d5c2bc0938d0169403b2b41827e1ebcec2582ae6 Mon Sep 17 00:00:00 2001 From: araharah-executor Date: Sat, 24 Oct 2026 05:09:27 +0800 Subject: [PATCH] feat(05-03): freeze guidance_sessions envelope as types-only contract (GREEN) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GuidanceSessionEnvelope: EXACTLY id, chat_id, route_snapshot, created_at (SessionClaim field-freeze law — a 5th property means drift) - GuidanceRouteSnapshot INLINE (D-07: one key, one TTL, ~2h): destination id/name, distanceM/durationS, polyline6 geometry (05-01 storage format), startedAtSec resume anchor - GUIDANCE_SESSION_TTL_SECONDS = 7200 — every Phase 6 write carries it - guidance: key-scheme doc header; zero runtime behavior (no binding, no function, no import — GE4 pins it) Note: header says 'No KV binding' rather than naming the KV type because the acceptance gate requires that token to appear NOWHERE in the file, comments included — same law, gate-clean wording. --- bot/src/kv/guidance-sessions.ts | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 bot/src/kv/guidance-sessions.ts diff --git a/bot/src/kv/guidance-sessions.ts b/bot/src/kv/guidance-sessions.ts new file mode 100644 index 0000000..135d39c --- /dev/null +++ b/bot/src/kv/guidance-sessions.ts @@ -0,0 +1,58 @@ +/* + * guidance-sessions.ts — the FROZEN Phase 6 contract, types-only (D-06/D-07). + * ------------------------------------------------------------------------ + * THIS MODULE SHIPS NO BEHAVIOR. No KV binding, no Env field, no wrangler + * namespace, no runtime function — the envelope is designed here (ROADMAP + * §Phase 5 GUID-05/06 upstream obligation) so Phase 6 builds its service + * on a frozen shape. Phase 6's service owns every write; every write + * carries expirationTtl: GUIDANCE_SESSION_TTL_SECONDS (~2h) and the + * snapshot is INLINE (one key, one TTL) — nothing outlives 2h, nothing is + * reconstructable after expiry (D-07, the location-pin TTL precedent). + * + * Key scheme: guidance: → JSON GuidanceSessionEnvelope + */ + +/** ~2h (D-06) — the envelope's every future write carries THIS value as its + * expirationTtl (Phase 6's privacy grep gate extends the TTL-law to it). */ +export const GUIDANCE_SESSION_TTL_SECONDS = 7200; + +/** + * The route snapshot — INLINE in the envelope (D-07): one KV key, one TTL. + * Nothing outlives 2h; nothing is reconstructable after expiry. Scoped to + * what a chat turn-card needs (GUID-05): the selected route's headline data + * + its geometry for the map snapshot render + the trip-start anchor for + * progress math on resume (GUID-06). + */ +export interface GuidanceRouteSnapshot { + /** The gazetteer id of the trip's destination. */ + destinationId: string; + /** The destination's display name (verbatim gazetteer name). */ + destinationName: string; + /** Selected route summary (meters / seconds). */ + distanceM: number; + durationS: number; + /** The SELECTED route's geometry, polyline6-encoded — the routing + * workspace's storage format (05-01), never a re-serialized array. */ + geometry: string; + /** Trip start, seconds-since-epoch — the resume-progress anchor (GUID-06). */ + startedAtSec: number; +} + +/** + * The guidance_sessions envelope — Phase 6's frozen contract (D-06). + * EXACTLY these four fields: a 5th property means drift (the SessionClaim + * law, bot/src/kv/sessions.ts). + * + * Key scheme: guidance: → JSON GuidanceSessionEnvelope + * Every write MUST carry expirationTtl: GUIDANCE_SESSION_TTL_SECONDS. + */ +export interface GuidanceSessionEnvelope { + /** Opaque session id — crypto.randomUUID() at Phase 6 mint (T-02-05 precedent). */ + id: string; + /** The Telegram chat the turn-cards push to (GUID-05). */ + chat_id: number; + /** The INLINE route snapshot (D-07) — never a reference or a second key. */ + route_snapshot: GuidanceRouteSnapshot; + /** Envelope creation, seconds-since-epoch. */ + created_at: number; +}