diff options
Diffstat (limited to 'frontend-cache-warming-lifecycle-handoff.md')
| -rw-r--r-- | frontend-cache-warming-lifecycle-handoff.md | 94 |
1 files changed, 0 insertions, 94 deletions
diff --git a/frontend-cache-warming-lifecycle-handoff.md b/frontend-cache-warming-lifecycle-handoff.md deleted file mode 100644 index 49bee0a..0000000 --- a/frontend-cache-warming-lifecycle-handoff.md +++ /dev/null @@ -1,94 +0,0 @@ -# FE handoff — CR-4 cache-warming lifecycle SHIPPED (+ CR-1 table, CR-2 scope) - -> **Courier doc** (backend → `../frontend`, via the user). Response to your -> `backend-handoff-cache-warming.md` (CR-4) and the open asks CR-1 / CR-2 in -> `backend-handoff.md`. Everything below is live on `bin/up` and verified with a -> headless probe (same flow as your `scripts/probe-cache-warming.ts` — re-run it to -> confirm; default-off means Phase C's toggle-enable branch now executes). -> -> **Contract bumps to re-pin:** `@dispatch/ui-contract` **0.1.0 → 0.2.0**, -> `@dispatch/transport-contract` **0.8.0 → 0.9.0**. `wire` unchanged (0.6.0). - -## CR-4a — warming now defaults OFF ✅ -A new conversation starts `enabled: false`, `nextWarmAt: null` — no warm is scheduled -until the user opts in via the toggle. Interval default is still 240s. Bonus fix: -re-enabling restores the conversation's PERSISTED interval (not the 240s default). -One caveat (pre-existing behavior, now fail-safe): opt-in is not yet re-hydrated -across a backend RESTART — after a restart a conversation reads disabled until -toggled again. Flag it if that matters to you and we'll add boot hydration. - -## CR-4b — post-warm updates now carry the FUTURE `nextWarmAt` ✅ -Root cause was notify-before-reschedule in the warmer. Fixed; additionally: -- after every automatic warm, the pushed `cache-warming-timer` payload is - `{ nextWarmAt: <future>, lastWarmAt: <just now> }` (probe: 2 warms @5s, both FUTURE); -- after `turn-sealed` the surface now pushes the fresh post-turn schedule (this was - the "still past after a real chat turn" case in your probe); -- on `turn-start` the surface pushes `nextWarmAt: null` (nothing scheduled while - generating — render as your "waiting…" state); -- if a warm completes with warming since-disabled, the update carries - `nextWarmAt: null`, never a stale past timestamp. -Your countdown can stay authoritative off `nextWarmAt`; the cosmetic past-value guard -should now be dead code. - -## CR-4c — `POST /conversations/:id/close` ✅ (the tab-close affordance) -New endpoint (no request body), `[email protected]`: - -```ts -interface CloseConversationResponse { - conversationId: string; - abortedTurn: boolean; // true iff an in-flight turn existed and was aborted -} -``` - -Semantics — exactly the asymmetry the user wanted: -- **Aborts any in-flight turn.** The kernel stops at the next event boundary; the - partial turn is PERSISTED and the turn SEALS normally — watchers receive - `done` (with `reason: "aborted"`) then `turn-sealed`, so your stream-derived - `generating` flag clears with no special-casing. Live-verified. -- **Stops + disables cache-warming** for the conversation (persisted OFF — reopening - the conversation later does not resume warming), and pushes a surface update - (`enabled: false`, `nextWarmAt: null`) to subscribers. -- **Idempotent**: closing an idle/unknown conversation is a 200 with - `abortedTurn: false`. -- Browser/socket disconnect and `chat.unsubscribe` are UNCHANGED — they still never - touch the turn or the warming schedule (your "keep running when the window closes" - half is regression-tested). -Wire this into `store.closeTab()`; `fetch`/`sendBeacon` both fine (CORS already -allows POST). - -## CR-4d — initial `surface` echo ✅ (no backend change was needed) -HEAD already echoes `conversationId` on the initial `surface` reply (shipped in the -per-conversation-scoping commit; unit-tested). We live-probed BOTH stacks today — -:24205 and your :25205 — and the echo is present. Your probe most likely ran against -a `bin/up2` instance booted before that commit (up2 freezes code at boot). Re-run -`bin/up2` and your probe; if you still see a missing echo, send us the raw frame. - -## CR-1 — Loaded Extensions table ✅ -The surface now emits the "Loaded" count stat plus ONE custom field: - -```ts -{ kind: "custom", rendererId: "table", payload: { columns, rows } } -// columns: ["Name", "Version", "Trust", "Activation"] -// rows: one per loaded extension (ALL trust tiers), cell-for-cell aligned -``` - -Typed payload is exported as `TablePayload` (+ `TABLE_RENDERER_ID`) from -`@dispatch/surface-loaded-extensions` if you want to narrow instead of duck-typing. -Note: `Version` cells all read `0.0.0` — manifests are genuinely unversioned today -(the optional data-quality item from your handoff; not done). - -## CR-2 — catalog `scope` flag ✅ (`[email protected]`) -`SurfaceCatalogEntry` gains `scope?: "global" | "conversation"`. Emitted today: -`loaded-extensions` → `"global"`, `cache-warming` → `"conversation"`. Treat ABSENT as -conversation-scoped (conservative — your current always-send-conversationId policy -remains correct for both). You can now skip re-subscribing `scope: "global"` surfaces -on conversation switch. - -## Suggested FE follow-ups (from your own queue) -- Re-pin + re-mirror `.dispatch/{ui-contract,transport-contract}.reference.md`. -- Wire `POST /conversations/:id/close` into the tab-close path. -- Extend `probe-cache-warming.ts`: assert default-off, post-warm FUTURE `nextWarmAt`, - and (new) close → `abortedTurn` + `done.reason === "aborted"`. -- The "waiting…" guard for a past `nextWarmAt` can stay as a belt-and-braces guard - but should never trigger now; `nextWarmAt: null` while generating is the real state - to render. |
