summaryrefslogtreecommitdiffhomepage
path: root/backend-handoff.md
diff options
context:
space:
mode:
Diffstat (limited to 'backend-handoff.md')
-rw-r--r--backend-handoff.md42
1 files changed, 41 insertions, 1 deletions
diff --git a/backend-handoff.md b/backend-handoff.md
index 2ddebe1..69deebf 100644
--- a/backend-handoff.md
+++ b/backend-handoff.md
@@ -5,7 +5,7 @@
> **From:** dispatch-web orchestrator · **To:** arch-rewrite orchestrator · **Courier:** the user.
> `lsp` does NOT span the repos (ORCHESTRATOR §5) — every cross-repo ask flows through here.
-_Last updated: 2026-06-10 — "Extensions" view shipped FE-side. ONE open ask: CR-1 (Loaded Extensions as a real multi-column table). The surface is already readable today; CR-1 is the enhancement that finishes the user's "nice table" request._
+_Last updated: 2026-06-11 — **Cache-rate fix + retention + CR-3 consumed FE-side** (from `frontend-cache-warming-handoff.md`): (1) per-turn cache rate now reads true on Claude (no FE change); (2) NEW cross-turn **expected cache (retention)** metric in the chat metrics bubble (`computeExpectedCachePct`/`viewExpectedCache`); (3) **CR-3 DONE & consumed** — the countdown is now AUTHORITATIVE off the surface's `cache-warming-timer` `nextWarmAt`/`lastWarmAt` (FE guessing dropped), history keyed off `lastWarmAt`, and `WarmResponse.expectedCacheRate` headlined on "Warm now"; (4) second "cache retention" `stat` parsed. transport mirror regenerated. **Earlier (same day):** `NumberField` + conversation-scoped subscriptions + "Cache Warming" sidebar view. Open asks: CR-1 (Loaded Extensions as a real multi-column table); CR-2 (optional catalog `scope` flag). **CR-3 is RESOLVED** (see §2)._
---
@@ -26,6 +26,19 @@ Endpoints in use (HTTP **24203**, WS **24205**, CORS `*`):
Mirrored in-repo for headless agents: `.dispatch/{ui-contract,wire,transport-contract}.reference.md`
(regenerate on any contract bump).
+**2026-06-11 re-mirror (cache-warming).** Both `ui-contract` and `transport-contract` were left at their
+existing versions by the backend (`[email protected]`, `[email protected]`) but gained ADDITIVE
+members; the `file:` deps already resolve them. The FE mirrors were regenerated to match:
+- `ui-contract.reference.md`: `NumberField` (`kind:"number"`) + optional `conversationId?` on
+ `Subscribe`/`Unsubscribe`/`Invoke`/`Surface`/`SurfaceUpdate`.
+- `transport-contract.reference.md`: `POST /chat/warm` (`WarmRequest`/`WarmResponse`) + the throughput
+ axis (`GET /metrics/throughput`, `ThroughputResponse`/`ThroughputModelStat`/`ThroughputPeriod`).
+- FE consumed: generic `number` renderer; protocol keyed by `surfaceId` carrying the focused
+ conversationId with a staleness rule (drop a `surface`/`update` echoing a non-current conversation;
+ a global no-echo reply is always accepted); store auto-subscribes every catalog surface with the
+ focused conversationId and re-scopes on conversation switch; `warmNow()` posts `/chat/warm` with the
+ conversation's current model name.
+
## 2. Open asks FOR THE BACKEND
### CR-1 — emit the **Loaded Extensions** surface as a true table
@@ -66,6 +79,33 @@ table (e.g. `Name | Version | Trust | Scope`), listing **all** loaded extensions
read `0.0.0` (unversioned). If real versions should appear in the table column,
bump each extension's manifest `version` — otherwise the column is all `0.0.0`.
+### CR-2 (optional, low priority) — a `scope` flag on the surface catalog entry
+
+The catalog (`SurfaceCatalogEntry`) carries no hint of whether a surface is GLOBAL or
+CONVERSATION-SCOPED, so the FE follows the handoff's "always send the focused `conversationId`"
+policy. That works (global surfaces ignore it; the FE's routing accepts the no-echo global reply),
+but it means the FE **re-subscribes every surface — including global ones like `loaded-extensions` —
+on every conversation switch**, which is needless churn (one redundant unsubscribe+subscribe round
+trip per global surface per switch; no user-visible bug, the old spec is retained so there's no
+flicker). An optional `scope?: "global" | "conversation"` on `SurfaceCatalogEntry` would let the FE
+skip re-subscribing globals on switch. **Not blocking** — only raise if cheap.
+
+### CR-3 — next-warm timestamp + manual-warm timer reset → **RESOLVED ✅ (backend `bfbad3a`, consumed FE-side)**
+
+Both asks shipped by the backend (no contract bump — `custom` escape hatch) and are now consumed:
+1. **`nextWarmAt` / `lastWarmAt` (epoch-ms)** arrive on the conversation-scoped `cache-warming` surface
+ as a `custom` field `{ rendererId: "cache-warming-timer", payload: { nextWarmAt, lastWarmAt } }`.
+ FE: `parseControls` reads them; the countdown is now derived straight from `nextWarmAt`
+ (`secondsUntilNext(nextWarmAt, now)`) and the history keys off `lastWarmAt` (`observeWarm`). The
+ old FE best-effort anchor/guess logic was DELETED.
+2. **Manual `POST /chat/warm` now re-arms the timer + pushes a surface `update`.** FE: dropped the
+ workaround of recording history from the HTTP response — history is driven authoritatively by the
+ surface's `lastWarmAt`; the HTTP `WarmResponse` is still used for the immediate "Warm now" feedback
+ line (now headlining `expectedCacheRate`). The generic surface-host does NOT render
+ `cache-warming-timer` (no registered renderer → graceful skip); the cache-warming feature owns it.
+
+(The standalone courier `backend-handoff-cache-warming-timer.md` is now historical — no open asks.)
+
## 3. Likely NEXT backend asks (heads-up, not yet requested)
- `GET /conversations` — conversation list / sidebar (history explorer / switcher); could also expose a