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; +}