You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Sep 8, 2026. It is now read-only.
Operator chose bounded cost and useful best-effort history over exact preservation. This replaces the global logical-history/overlap engine and queue-command transaction from the old draft. Queue/preferences are already handled by #960. This phase reads one existing Blob object per page request and builds useful Wasm navigation over those pages, without promising exact global history order/count/height.
Finding
Resolved decision
Exact global merge consumes too much CPU/memory
Per-object raw-row coordinates and explicit earlier-snapshot boundary; no full-chain reconstruction
Existing Load earlier only slices resident array
Actual authenticated object-page fetch; fresh host starts tail-only
Global scrollbar implies an exact document
Scrollbar position is approximate within loaded source snapshot, older/newer snapshot controls explicit; latest jump always direct
Streaming updateLast corrupts viewed old page
Separate latest bounded buffer and visible history ring; no live bridge mutations to old rows
Ambiguous duplicate old snapshots
Bounded exact (role,text) repeat suppression; omissions/repeats acceptable, progress always based on source indices
Scores: correctness4, performance5, architecture4, testing5, security4, reusability5, parent adherence5, layers5, docs5, cap governance5, UI/layout4. Administrative cloud ops N/A. HANDOFF-READY after dependency. No code/tests run in review. Existing sessionWindow, host hydrate/apply, Blob scope/prev semantics, ui/state/scroll, bridge/build/CI and palette baseline read.
Goals / scope / ownership
G1: tail-only host fetches older display rows from actual Blob-backed page API, not hidden full array.
G2: wheel/touch/keyboard boundary loads, approximate position seek and direct latest work in canvas with stable anchor/draft/Stop.
G3: only latest+viewed buffers retained; one object read/request; no automatic chain chase.
new int/viewport-pages.int.test.ts, shared driver/loadBridge
No worker storage/inference changes, physical layout/index, source-span rope, byte-Range JSON, global exact total, queue mutation endpoint, full-history rehydration or new DOM UI. Do not resurrect old parent precision gates.
1. Per-object page protocol
Extend existing latest JSON route:
GET /api/sessions/:id/viewport?objectId=<id>&beforeRow=B or ...&atRow=R.
Auth owned session every request. Validate object id scope with existing isObjectIdBoundTo; never client URL, SDK historical run or forged cross-session pointer. Current/previous owned transcript object ids permitted, like existing signed-object read seam; possession of a foreign id never grants access.
One Blob read per request, max existing8 MiB object; no prev read in the same request. Optional read work uses #9595s budget; timeout/error leaves current view, no automatic retries.
Coordinates are raw source array boundaries, not global canonical ordinal, SSE cursor, or number of coalesced rows. beforeRow=B examines up to512 raw rows immediately before B; default B=messages.length. atRow=R examines up to512 starting at R, clamped to valid range. Unknown/conflicting selectors400.
Scan candidates newest-first where choosing a tail; validate roles/text, project/excerpt to existing bridge bound and snapshot byte budget. If some candidates are invalid/duplicate/overbudget, return fewer rows and the next raw boundary reflecting examined/consumed candidates. Never return the same cursor repeatedly. A maximum-size single row gets a labeled excerpt; no silent zero-progress omission loop.
Return {version:1,objectId,rows,sourceRange:{start,end,totalRows},nextBeforeRow,prevObjectId?,historyComplete:false,incomplete:true} plus source-index key on each row/group. totalRows is this object's array length only. prevObjectId is syntax+scope-checked before returning, not fetched. A true flattened root has no prev.
At boundary0, next explicit older action may fetch prevObjectId. Never automatically walk the chain until unique rows fill a screen. Show a neutral “Earlier stored snapshot; some rows may repeat” boundary label when moving objects. Empty/all-duplicate page advances cursor but doesn't automatically chase another page without new user scroll/action.
Host records visited ids up to existing256 walk guard and refuses loop/repeat traversal while retaining current paint. Missing/unreadable/foreign source uses typed error/stale state, not empty successful complete history. Standard401/404/400/503, no payload logging, private/no-store.
No persisted generation manifest or body digest. Append-only source assumption follows production Blob; if a BYO object changes, range clamp/restart keeps UI usable, exact stale-version detection not guaranteed.
Small duplicate/group handling
Use bounded currently-held latest/viewed + arriving candidates for exact same (role,text) display suppression; preserve raw source cursor even if all dropped. Same-text legitimate messages may collapse and some encoded tool groups may repeat; accepted. Do not invoke prefix/suffix legacy matcher. Hash/index only bounded candidate strings, no global dedup set.
Coalesce only within a returned page using existing tool encoding rails and retain source-span anchors; don't merge across page/object boundaries and invent global ids. Stable display key {objectId,rawStart,rawEnd}; if byte fitting excerpts text, keep key and flag. No exact full document row count required.
2. Host window controller
State: {sessionEpoch,viewedObject/range,latestWindow,viewedWindow,followLatest,pendingTarget,loading,error,visitedIds}. One active page/bootstrap request per epoch; coalesce desired seek, abort/ignore obsolete response. Keep at most two bounded2048-row windows with total4 MiB serialized accounting. Incoming temporary page released after bounded merge, no all-pages cache or closure referencing prior full Blob arrays.
Reading history: live reducer updates latest buffer without calling bridge updateLast/push on the historical ring. Use a narrow display sink/suppress-paint adapter, not a second entire HarnessBridge implementation. Parser finalText/assistant/tool/thinking buffers also bounded to their existing display limits; do not retain whole turn through a hidden accumulator.
Before paging/window shift capture first visible source key + intra-row pixel offset. Preserve overlapping anchor; on explicit seek anchor target. If anchor absent due approximation/dedup, nearest remaining row/edge is acceptable and marked page boundary; no empty/frozen UI trying to prove exactness.
Local v2 saves latest buffer, not viewed old page. New/Clear/switch reset visited state, ignore late response, preserve current selected-session queue semantics.
3. Wasm navigation / bridge
Use next unused protocol version (baseline24; choose next free at implementation) and paired required exports/TS wrappers/build whitelist. Add scalar state/request/anchor exports:
setHistoryWindow(start,end,totalRows,loading,hasEarlierSnapshot,followLatest) for current source object's raw coordinates.
pending request kind older/newer/seek/latest + target raw ordinal; host polls/acks. Existing Load earlier export becomes alias to this controller, not separate in-memory implementation.
first-visible row/source anchor readback and pending anchor restore fields. Host supplies row source-index metadata as bridge write-time scalar entries; no arbitrary object id parsing in frame. Apply anchor after row geometry is measured, then clamp.
Boundary wheel/touch/keyboard issues a single request while more is available; loading/ack prevents repeated frame requests. Explicit earlier-snapshot/retry/latest controls keep navigation possible if empty/duplicate content has no scroll range. Newer can return within current object or the most recent bounded visited source reference; no full forward history reconstruction. Preserve existing browser-reserved keyboard keys.
Use a scrollbar/position control for current snapshot's raw range, clearly not a global archive total. Drag maps to raw row seek, local scroll stays pixels. Loaded-page/source boundary cues communicate approximation. Variable-height rows/images/math/expansion/resize remeasure and restore anchor; exact offscreen height not promised.
Palette from existing TS/Zig modules: TEAL chrome, WARM only intentional emphasis, EMBER actual errors. Incomplete history/boundaries neutral System/TEAL. ≥existing44px touch target metrics, ~390px completion, composer/Send/Stop/status fixed across loading/error. Preserve draft/focus, FIFO/pause/promote. No app GPA allocation or network in ui.frame; metadata decoding/work at bridge write time, fixed buffers/per-slot geometry. Small source-index arrays sized to existing ring, not history size.
Caps
New performance-first defaults authorized by operator; no existing cap is changed.
Cap / carrier
Value / rationale
Location
Page candidates
Existing512 raw source rows/request
HISTORY_PAGE, page service; output may coalesce/drop
Server cost per page linear in one object plus at most512 candidates, no exact overlap CPU. Browser may reread same8 MiB object for multiple pages; acceptable initial cost, no cache/index subsystem prerequisite. Measure calls/bytes to detect accidental full-chain work, not demand perfect latency.
Test matrix
#
Required case / evidence
P1
Flatten root and overlapping prev-bearing heads: correct per-object raw ranges, no hidden chain merge; user-driven prev only
P2
before/seek bounds, malformed/duplicate/overbudget rows, Unicode/oversized single row: always source-cursor progress or explicit boundary/error
P3
Auth foreign object/session, missing/deleted source, loop/max visited, read timeout: no leak or auto chase, current ring retained
P4
Tail-only host older request→actual scoped page route/Blob read→real Wasm ring; spy full-history repo.get never called
P5
Repeated page/seek/latest with bounded windows, exact-repeat suppression, empty duplicate page explicit action; no unbounded request loop
P6
Live text/tools/thinking while old history viewed, Stop/cancelling/queue; viewed ring unchanged, latest advances, raw cursor unaffected
P7
Anchor/source-index bridge, geometry reset/resize/expansion, pending request ack; pure Zig module test-rich + real bridge readback
P8
Source switch/New/Clear/late response and local cache latest-not-viewed; partial view cannot full-PUT or seed promptHistory
npm test, npm run test:int, npm run typecheck, npm run build; build-harness test-rich/build+current required exports
P11
Matched PR/main artifact supply via int-durable/Vercel; mismatched/missing Wasm fails closed
P12
Hosted desktop/~390px touch/wheel/keyboard seek/retry/latest, draft/focus, composer/status/Stop stable; no DOM transcript
All delivered tests ordinary green, no skipped/expected-failure mask. Browser visual smoke is additive; unit page slices alone don't prove network→controller→real ring. No tests run during review.
Docs / ops / order / DoD
Surface
Required current-behavior update
Session guide
Per-object approximate history and duplicate/gap behavior, live/latest separation and failures
Stream guide
Live buffer while viewing history, raw cursor independent of page indices
One-object cost,512 candidates, two-window bytes, no global count/height claim
AGENTS
New controller/bridge/page ownership and bounded best-effort rule
README
Concise on-demand history feature/link
SECURITY
Per-request owned-session/scoped-object checks, no writes or keys
native/harness/README.md
Actual next-version exports, page source indices/anchors/navigation
.env.example
N/A no secret/env/feature flag
No admin migrate/backfill/index/cleanup job. Use existing configured runner build-harness for Zig, same-repo PR artifact→int-durable and main artifact→Vercel. Paired host/Wasm rollback; no transcript deletion/key purge/laptop steps. Docs timeless, not phase diary.
G1–G4/P1–P11 green and P12 recorded; actual cloud-page→real ring, finite cost.
No global exact matcher/index, no auto full-chain chase, no head-only source falsely called full history.
Live cursor/Stop/FIFO/draft safe while history read; partial rows never uploaded/seeded.
Approximate seek/anchor and boundary/retry/latest usable on desktop/mobile; palette/geometry/frame budget intact.
New paired protocol and current artifact gates, docs complete.
Prior children shipped; PR Fixes #962 and Fixes #553 after all three leftovers delivered; Refs #958 #924. Parent closes separately as tracker.
Open questions: none. Accepted tradeoffs: same object read on multiple page requests, overlapping snapshots may repeat/drop some rows, approximate per-snapshot rather than whole-archive positioning, source unavailable stops earlier navigation. These are the approved sensible defaults, not grounds to block for a perfect archive.
Plan header
mainb31ca512e7ddee162fa8313cc52f549822e2d23a, then dependencies;feat/a5-bounded-history-navigationReview notes (2026-09-07)
Operator chose bounded cost and useful best-effort history over exact preservation. This replaces the global logical-history/overlap engine and queue-command transaction from the old draft. Queue/preferences are already handled by #960. This phase reads one existing Blob object per page request and builds useful Wasm navigation over those pages, without promising exact global history order/count/height.
(role,text)repeat suppression; omissions/repeats acceptable, progress always based on source indicesScores: correctness4, performance5, architecture4, testing5, security4, reusability5, parent adherence5, layers5, docs5, cap governance5, UI/layout4. Administrative cloud ops N/A. HANDOFF-READY after dependency. No code/tests run in review. Existing sessionWindow, host hydrate/apply, Blob scope/prev semantics, ui/state/scroll, bridge/build/CI and palette baseline read.
Goals / scope / ownership
lib/sessions/viewportRead.ts, new purelib/sessionHistoryPage.ts;app/api/sessions/[id]/viewport/route.tslib/viewportNavigation.ts,lib/sessionRepository.ts,lib/sessionWindow.ts,HarnessHost.tsx,turnApply.tsnative/harness/src/ui/history_navigation.zig,ui/{state,scroll}.zig,ui.zig,bridge.ziglib/harnessBridge.ts,native/harness/build.zig, protocol testsint/viewport-pages.int.test.ts, shared driver/loadBridgeNo worker storage/inference changes, physical layout/index, source-span rope, byte-Range JSON, global exact total, queue mutation endpoint, full-history rehydration or new DOM UI. Do not resurrect old parent precision gates.
1. Per-object page protocol
Extend existing latest JSON route:
GET /api/sessions/:id/viewport?objectId=<id>&beforeRow=Bor...&atRow=R.isObjectIdBoundTo; never client URL, SDK historical run or forged cross-session pointer. Current/previous owned transcript object ids permitted, like existing signed-object read seam; possession of a foreign id never grants access.prevread in the same request. Optional read work uses #9595s budget; timeout/error leaves current view, no automatic retries.beforeRow=Bexamines up to512 raw rows immediately before B; default B=messages.length.atRow=Rexamines up to512 starting at R, clamped to valid range. Unknown/conflicting selectors400.{version:1,objectId,rows,sourceRange:{start,end,totalRows},nextBeforeRow,prevObjectId?,historyComplete:false,incomplete:true}plus source-index key on each row/group.totalRowsis this object's array length only.prevObjectIdis syntax+scope-checked before returning, not fetched. A true flattened root has no prev.prevObjectId. Never automatically walk the chain until unique rows fill a screen. Show a neutral “Earlier stored snapshot; some rows may repeat” boundary label when moving objects. Empty/all-duplicate page advances cursor but doesn't automatically chase another page without new user scroll/action.Small duplicate/group handling
Use bounded currently-held latest/viewed + arriving candidates for exact same
(role,text)display suppression; preserve raw source cursor even if all dropped. Same-text legitimate messages may collapse and some encoded tool groups may repeat; accepted. Do not invoke prefix/suffix legacy matcher. Hash/index only bounded candidate strings, no global dedup set.Coalesce only within a returned page using existing tool encoding rails and retain source-span anchors; don't merge across page/object boundaries and invent global ids. Stable display key
{objectId,rawStart,rawEnd}; if byte fitting excerpts text, keep key and flag. No exact full document row count required.2. Host window controller
State:
{sessionEpoch,viewedObject/range,latestWindow,viewedWindow,followLatest,pendingTarget,loading,error,visitedIds}. One active page/bootstrap request per epoch; coalesce desired seek, abort/ignore obsolete response. Keep at most two bounded2048-row windows with total4 MiB serialized accounting. Incoming temporary page released after bounded merge, no all-pages cache or closure referencing prior full Blob arrays.3. Wasm navigation / bridge
Use next unused protocol version (baseline24; choose next free at implementation) and paired required exports/TS wrappers/build whitelist. Add scalar state/request/anchor exports:
setHistoryWindow(start,end,totalRows,loading,hasEarlierSnapshot,followLatest)for current source object's raw coordinates.Boundary wheel/touch/keyboard issues a single request while more is available; loading/ack prevents repeated frame requests. Explicit earlier-snapshot/retry/latest controls keep navigation possible if empty/duplicate content has no scroll range. Newer can return within current object or the most recent bounded visited source reference; no full forward history reconstruction. Preserve existing browser-reserved keyboard keys.
Use a scrollbar/position control for current snapshot's raw range, clearly not a global archive total. Drag maps to raw row seek, local scroll stays pixels. Loaded-page/source boundary cues communicate approximation. Variable-height rows/images/math/expansion/resize remeasure and restore anchor; exact offscreen height not promised.
Palette from existing TS/Zig modules: TEAL chrome, WARM only intentional emphasis, EMBER actual errors. Incomplete history/boundaries neutral System/TEAL. ≥existing44px touch target metrics, ~390px completion, composer/Send/Stop/status fixed across loading/error. Preserve draft/focus, FIFO/pause/promote. No app GPA allocation or network in
ui.frame; metadata decoding/work at bridge write time, fixed buffers/per-slot geometry. Small source-index arrays sized to existing ring, not history size.Caps
New performance-first defaults authorized by operator; no existing cap is changed.
HISTORY_PAGE, page service; output may coalesce/dropVIEWPORT_CACHE_MAX_BYTESServer cost per page linear in one object plus at most512 candidates, no exact overlap CPU. Browser may reread same8 MiB object for multiple pages; acceptable initial cost, no cache/index subsystem prerequisite. Measure calls/bytes to detect accidental full-chain work, not demand perfect latency.
Test matrix
npm test,npm run test:int,npm run typecheck,npm run build; build-harness test-rich/build+current required exportsAll delivered tests ordinary green, no skipped/expected-failure mask. Browser visual smoke is additive; unit page slices alone don't prove network→controller→real ring. No tests run during review.
Docs / ops / order / DoD
native/harness/README.md.env.exampleNo admin migrate/backfill/index/cleanup job. Use existing configured runner
build-harnessfor Zig, same-repo PR artifact→int-durable and main artifact→Vercel. Paired host/Wasm rollback; no transcript deletion/key purge/laptop steps. Docs timeless, not phase diary.Implement page protocol/service/tests → bounded host controller/display sink → paired bridge/Wasm requests/anchors/control → real-Wasm page/live integration → docs/full gates/hosted smoke. One PR only.
Fixes #962andFixes #553after all three leftovers delivered;Refs #958 #924. Parent closes separately as tracker.Open questions: none. Accepted tradeoffs: same object read on multiple page requests, overlapping snapshots may repeat/drop some rows, approximate per-snapshot rather than whole-archive positioning, source unavailable stops earlier navigation. These are the approved sensible defaults, not grounds to block for a perfect archive.