From d66585333ee5764700c67a81eaec015b0026f8f1 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. --- src/app/store.svelte.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/app') diff --git a/src/app/store.svelte.ts b/src/app/store.svelte.ts index 379805f..999f2be 100644 --- a/src/app/store.svelte.ts +++ b/src/app/store.svelte.ts @@ -25,7 +25,7 @@ import { subscribe as protocolSubscribe, unsubscribe as protocolUnsubscribe, } from "../core/protocol"; -import type { ChatStore, MetricsSync } from "../features/chat"; +import type { ChatStore, HistorySync, MetricsSync } from "../features/chat"; import { createChatStore } from "../features/chat"; import type { ConversationCache } from "../features/conversation-cache"; import { createConversationCache } from "../features/conversation-cache"; @@ -111,12 +111,13 @@ export interface CreateAppStoreOptions { localStorage?: Storage; } -function createHistorySync( - httpBase: string, - fetchImpl: typeof fetch, -): (conversationId: string, sinceSeq: number) => Promise { - return async (conversationId: string, sinceSeq: number) => { - const url = `${httpBase}/conversations/${encodeURIComponent(conversationId)}?sinceSeq=${sinceSeq}`; +function createHistorySync(httpBase: string, fetchImpl: typeof fetch): HistorySync { + return async (conversationId, sinceSeq, window) => { + let url = `${httpBase}/conversations/${encodeURIComponent(conversationId)}?sinceSeq=${sinceSeq}`; + // CR-5 windowing (transport-contract@0.10.0): both must be positive + // integers when present (the server 400s otherwise; callers guarantee it). + if (window?.limit !== undefined) url += `&limit=${window.limit}`; + if (window?.beforeSeq !== undefined) url += `&beforeSeq=${window.beforeSeq}`; const res = await fetchImpl(url); if (!res.ok) { throw new Error(`History sync failed: ${res.status}`); -- cgit v1.2.3