From 1776912f39830060f47aeca2cf452674786da36d Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Fri, 12 Jun 2026 19:00:29 +0900 Subject: feat(chat): consume CR-5 history windowing — server-windowed cold loads + show-earlier backfill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-pinned transport-contract@0.9.0->0.10.0 + wire@0.6.0->0.6.1 (reply frontend-history-windowing-handoff.md); re-mirrored both .dispatch references. - HistorySync port gains optional { limit?, beforeSeq? } (CR-5 params); the app's createHistorySync appends them to GET /conversations/:id. - COLD-cache fresh load now fetches ?sinceSeq=0&limit= — a huge conversation no longer ships whole to show 192 chunks. A warm-cache tail sync stays unwindowed (windowing a tail that outgrew the limit would leave a silent seq gap behind the cache). - hasEarlier now derives from the wire@0.6.1 CONTRACT (1-based gap-free seqs): loaded window starting above seq 1 => older history exists — covering both locally-trimmed AND server-windowed transcripts (the watermark stays as the merge floor only). - showEarlier(): local cache first; when the cache doesn't reach far enough back, backfills the missing older run via ?beforeSeq=&limit= and persists it (next page-in is local). latestSeq windowed-read caveat is satisfied structurally (tail cursor derives from the cache's max seq). - live-probe: +6 CR-5 checks (seq origin, newest-k ascending, short-chat exactness, beforeSeq paging, 400 validation x2). NOT yet run live — backend was down at commit time; run pending. - backend-handoff.md: CR-5 RESOLVED, pins/mirrors current. 602 tests green x2. --- .dispatch/wire.reference.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to '.dispatch/wire.reference.md') diff --git a/.dispatch/wire.reference.md b/.dispatch/wire.reference.md index 40f94cf..1d761bf 100644 --- a/.dispatch/wire.reference.md +++ b/.dispatch/wire.reference.md @@ -4,8 +4,14 @@ > types WITHOUT following the `file:` dep symlink out of this repo (which hangs on a permission > prompt). Your CODE still imports `@dispatch/wire` normally — this file is for READING only. > -> **Orchestrator:** SNAPSHOT of `wire@0.6.0` (the metrics types below + the `user-message` turn event -> shipped + version-bumped). Regenerate whenever `@dispatch/wire` changes. +> **Orchestrator:** SNAPSHOT of `wire@0.6.1` (doc-only bump: the 1-based gap-free seq guarantee +> codified on `StoredChunk`). Regenerate whenever `@dispatch/wire` changes. +> +> **2026-06-12 delta (CR-5 history windowing — package bumped `0.6.0` → `0.6.1`, DOC-ONLY):** the +> per-conversation `seq` numbering is now a WRITTEN CONTRACTUAL GUARANTEE on `StoredChunk`: +> **1-based, monotonic, gap-free** — a conversation's first chunk is always `seq === 1` and +> numbering never skips. A client holding only a windowed suffix of the log derives "older chunks +> exist server-side" purely from `oldestLoaded.seq > 1` (no `earliestSeq`/`hasOlder` field exists). > > **2026-06-12 delta (CR-3 user-message handoff — package bumped `0.5.0` → `0.6.0`, ADDITIVE):** adds a > new `AgentEvent` union member `TurnInputEvent` (`{ type: "user-message"; conversationId; turnId; text }`) @@ -168,11 +174,17 @@ export interface ChatMessage { /** * A persisted chunk plus its sync metadata. The append-only conversation log - * stamps every chunk with a monotonic, gap-free, per-conversation `seq` (the - * sync cursor, assigned in append order) and records the `role` of the message - * it belongs to. This makes a flat seq-ordered stream both incrementally - * syncable ("give me chunks after seq N") and regroupable into messages by the - * client. `chunk` is the content unit — `Chunk` carries no storage/sync cursor + * stamps every chunk with a **1-based**, monotonic, gap-free, per-conversation + * `seq` (the sync cursor, assigned in append order) and records the `role` of + * the message it belongs to. This makes a flat seq-ordered stream both + * incrementally syncable ("give me chunks after seq N") and regroupable into + * messages by the client. + * + * The 1-based start is a CONTRACTUAL GUARANTEE (not an implementation detail): + * a conversation's first chunk is always `seq === 1` and numbering never skips, + * so a client holding only a windowed suffix of the log can derive "older + * chunks exist server-side" purely from `oldestLoaded.seq > 1` — no separate + * has-older flag is needed (or provided). `chunk` is the content unit — `Chunk` carries no storage/sync cursor * (`seq` lives here on the envelope, not on the chunk, since it is assigned by * the store and the provider has no use for it). A chunk MAY still carry * generation provenance assigned at production time (e.g. a tool chunk's -- cgit v1.2.3