Skip to content
This repository was archived by the owner on Sep 8, 2026. It is now read-only.
This repository was archived by the owner on Sep 8, 2026. It is now read-only.

plan: A5 viewport — bounded best-effort F5, slim cache, on-demand history (tracking parent) #958

Description

@btipling

Plan header

Field Value
Status HANDOFF-READY — tracking only, never implemented
Date 2026-09-07
Parent / source #548 architecture umbrella; #553 viewport spike
Baseline main b31ca512e7ddee162fa8313cc52f549822e2d23a
Layers / reusability Scoped backend readers, DOM host I/O, Wasm product UI; existing provider seams, no deployment-specific infrastructure
Administrative Production mutate? None — no migration/backfill/env/secret cutover; ordinary small metadata updates only
Cloud path Existing Git deploy, int-durable, build-harness→artifact→Vercel
Living docs Session/stream/limits/feature-divide, AGENTS, README, SECURITY; native protocol guide in navigation phase

Review notes — operator decision and unblock (2026-09-07)

Operator explicitly chose: “we don't need perfect precision, best effort is good enough. default to good perf and sensible defaults … History is a nice to have … Pick reasonable defaults, don't block on open questions, resolve them.”

This replaces the previous precision-first plan. Its A/B/C/D and L1/L2 prerequisites are removed, not deferred mandatory work. We will improve the broken F5 path with bounded recent display and honest omissions. No perfect archive, legacy alignment proof, certificate subsystem, exact overlap engine, global CAS or transactional queue journal is required.

Preserved hard requirements: authentication/session isolation, server secrets, no implicit rerun/cancel, no partial viewport uploaded as canonical transcript, real-Wasm e2e tests and Wasm-only product UI. Existing durable storage/inference/step/queue/bridge caps remain unchanged.

Previous blocker Resolved practical choice
Ambiguous old prompt/run splice Don't reconstruct it. Show sampled stream rows or latest head; missing historical prompt acceptable.
Expensive overlap matcher No cold prev-chain walk; later pages inspect one scoped object/request and use bounded exact-repeat suppression only.
Checkpoint certificates/witnesses Deleted: no persisted displayResume, digest/step witness, run-input decryption or producer provenance. SDK handles normal stream encryption.
Infinite chase of live tail One capped sample, one final tail probe, explicit skipped gap, then live.
Full-history transaction safety project Existing worker persistence stays unchanged. Prevent new partial uploads; preferences/queue use one narrow field-only patch preserving current stored pointer/body. No candidate/source-generation CAS or worker overhaul.
Exactly-once queue/offline archive Desired-state queueMirror with explicit[] and best-effort LWW; v2 write success before v1 removal, no cloud rescue/ack archive protocol.

Verdict HANDOFF-READY, tracking only. Scores: correctness4, performance5, architecture4, testing4, security4, reusability5, parent adherence5, layers5, cap governance5, living docs5, UI/layout4; administrative cloud ops N/A. No application code or tests run in review. #959–#962 revised and individually marked HANDOFF-READY with sequential dependencies; no stale precision-first child remains.

Phase map — one issue per PR

Phase Reviewed issue Deliverable Implement after Close
1 #959 — HANDOFF-READY Bounded latest-head/tail read + snapshot-first/indexed transport Now Own child only
2 #960 — HANDOFF-READY Actual host F5 fix + partial-view metadata sync #959 merged Own child + #924 with real-Wasm close gate
3 #961 — HANDOFF-READY Disposable bounded local cache #960 merged Own child
4 #962 — HANDOFF-READY On-demand per-object history + Wasm navigation #961 merged Own child + #553 after all leftovers

Parent/spike never implementation targets. Start with #959. The F5 bug is fixed by #960; it does not wait for cache/scrollbar. Dependencies are normal implementation order, not open design questions. One PR/issue, no hidden multi-PR phase; explicit re-plan if a split is truly needed. No merge authorization is implied by planning.

Goals

  1. Live F5/new tab/switch retains or paints useful recent rows without prompt rewind or chronological replay.
  2. Historical sampled thinking is omitted; events after server handoff count as live, even if they continue an old segment.
  3. Busy/Stop works during recovery and running/cancelling attach; no duplicate POST/implicit cancel.
  4. Local saves are bounded latest-view caches, not full-history stringify.
  5. Earlier rows fetched on demand with useful approximate navigation, finite work and direct latest jump.
  6. Display data stays separate from model seed and canonical transcript writes.

Shared design contracts

Bounded recent read / live handoff (#959)

  • Owned session/current envelope run match before Workflow stream access; bound Blob object ids, existing auth/middleware. No historical-run browser APIs or raw run/step-input reads.
  • Public SDK4.8.4 decoded readable + tail index, no new dependency/version. Capture H0, sample [max(0,H0−2048),H0) within 2048frames/8MiB/5s optional recovery. One latest Blob head only, no prev reads or exact source merge.
  • Select sampled assistant/tool/error rows if useful, otherwise latest head, otherwise replace:false retains cached paint. Don't infer missing prompt/run splice or concatenate ambiguous head/sample.
  • Final tail probe once, max1s; H=max(H0,fresh tail) or H0 fallback. Omitted sample/backlog gaps flagged. No repeat catch-up/re-probe loop, no reader-token registry. Sample readers closed; live reads H forward on same negotiated response.
  • Optional source budget does not cancel inference/live route. Cancel readers/stop app work on budget/abort; finite noncancellable provider calls may complete but late results cannot repaint or spawn more work. One decoded huge SDK frame may allocate before discard; document, don't build custom SDK crypto/framing to avoid all risk.
  • Snapshot contains historyComplete:false, incomplete/source/gap flags, bounded rows and resumeIndex. Cursor is transport position, not proof all earlier history is visible. Clip display excerpts with note, source untouched.
  • Drop all pre-H sampled reasoning bodies. Post-H reasoning paints normally. Reset host live row buffers at handoff and append fresh continuation rows; modest fragments/duplicates accepted. Full done.text is not appended over already shown tail.
  • viewportVersion=1: state/snapshot/indexed turn_event/end/error records; POST indexed too for hot-resume parity. Known malformed stored frame emits typed skipped record with index, unknown decoder position closes. Synthetic lifecycle controls never consume stored positions. Unnegotiated old routes remain unchanged.

Production host + metadata-only sync (#960)

All boot/login/switch/cold-Send use the actual shared controller and #959 single snapshot-first response. State restores Busy/Stop before optional recovery finishes. No prefix wipe, full repo reconstruction or second cold raw GET. Hot same-heap uses applied absolute cursor. Epochs discard stale session data; abort reader ≠ cancel; explicit Stop uses existing API. Existing FIFO/pause/no-promote-on-Stop/error laws remain.

Every new partial view is barred from full put/trim/mint/upload and from promptHistory. New production host updates only small fields through:

PATCH /api/sessions/:id/envelope {selectedModel?, reasoningEffort?, queueMirror?}.

Concrete refinement: add a narrow store patchEnvelopeMetaFields, implemented by one Redis Lua field patch and memory equivalent. Read current record inside operation, change only validated allowed fields, preserve current transcriptPointer/all worker siblings/body/TTL, no-create on missing, existing meta/body bounds. Reject arbitrary meta/messages/pointers/runStatus. No stale host full-envelope copy, source-generation CAS, retry journal or B7/B8 refactor. Existing concurrent old worker metadata overwrite remains best effort; no false guarantee that old workers are instantly upgraded. This operation preserves the current pointer while applying user preferences; it is not a history transaction system.

meta.queueMirror is JSON desired array, existing16×5000 bounds; valid[] explicitly clears, omit preserves. Reads prefer valid present mirror over legacy Blob queue, else fallback. New-code store upserts copy it forward on omit. Host computes queue with existing helpers, strips promoted prompt once before POST, restores on pre-start failure, never strips again server-side. Queue-internal Wasm edits still limited to current host-observed behavior. One coalesced request per session, latest desired state wins; response-loss replay of the same array is idempotent, no append command duplicates. Cross-tab/old-code LWW race is accepted; no exactly-once claim.

Worker-owned cwd/bind/provider/usage/notes/model/compaction remain server-written. Persona still selected through existing New-session flow, not a new PATCH field. Rare next real turn lacking a server seed sends raw prompt without partial display sidecar; normal A4/model seed path unchanged. Missing legacy context is accepted rather than treating truncated viewport as full history.

Disposable cache (#961)

V2 invincible.harness.viewport.v2: safe carriers/queue + latest rows under1MiB, always partial. Tail-first at most2048 candidates, account escaped JSON once, excerpts or pointer-only under pressure. No full source stringify/mutation or new token-save timer. Save latest, not currently viewed old page.

Prefer valid v2, fallback v1/empty. One old v1 parse allowed. Write bounded v2 successfully then remove v1, no cloud reconciliation/backfill rescue. Failed write preserves v1 unchanged; removal failure leaves harmless stale v1. Old history outside v2 can be lost under operator decision. No guaranteed offline archive; queue/current small prompt kept where budget allows. Clear removes both/invalidates late epochs. Cache cannot full-PUT or seed model.

On-demand history (#962)

Each request reads one scoped object, source messages raw coordinates; up to512 candidates at beforeRow=B (ends at B) or atRow=R (starts at R). Return raw source start/end/total for this object only, source-index row keys, next cursor and validated prevObjectId without fetching it. Byte limits may reduce output; cursor always advances over examined/skipped rows.

At source boundary0 a subsequent explicit scroll/action may visit prev; no auto chain scan until a page fills. Visited ids≤existing256 guard detects loops. Missing/foreign/timeout stops earlier navigation with current paint. Local bounded exact (role,text) repeat suppression only; duplicates/gaps/repeated legitimate text collapse accepted. No global canonical matcher/row count or archive-height promise. Source object may be reread per page—acceptable initial cost, not a blocker for a physical index project.

Host retains bounded latest+viewed windows, one active demand per epoch. Live updates latest state without mutating old viewed ring; parser/apply buffers bounded too. Anchor first visible source key+pixel offset; nearest available fallback acceptable. Jump latest one valid-buffer swap or acquisition; sends follow latest. V2 saves latest. Preference/queue sync remains #960, no new queue endpoint here.

Wasm owns boundary/seek/retry/latest controls and current-snapshot approximate scrollbar. Paired scalar request/state/source-anchor exports, next unused protocol (baseline24), TS wrapper/build whitelist. No network/app GPA work in frame; metadata decoded on write, fixed ring-sized geometry arrays.

Baseline evidence

Read on main b31ca512…: cold helpers hardcode0; host prefix wipes; turnApply thinking always paints/counts callbacks; stream route current-session/run scope already enforced. SDK public absolute readable/tail helper verified, encryption internal. Blob heads overlap or flatten; existing reconstruction materializes chain and invokes costly heuristic matcher. Full local stringify and whole-snapshot metadata saves verified. Queue helpers16×5000 and desired mirror semantics read. Session meta replace/copy-forward validation and Redis get/set baseline read; narrow field patch is new explicit scope. Current real-Wasm int separate from npm test; bridge/CI/export/palette files read. No code/tests run during planning.

Caps and approval

Operator authorized reasonable new performance budgets/best-effort omissions. No existing durable/model/step/queue/bridge limit changes. Any future change to an existing value still needs explicit approval and wire defense. New view budgets aren't source retention quotas.

Cap Value Rationale / location
VIEWPORT_TAIL_MAX_FRAMES 2048 Constant recent stream sample, shared caps
VIEWPORT_RECOVERY_MAX_BYTES 8MiB decoded frames Generous work bound; reasoning bytes count even when discarded
VIEWPORT_RECOVERY_MAX_MS 5000ms Optional history not minutes of recovery; inference unaffected
VIEWPORT_FINAL_PROBE_MAX_MS 1000ms One final refresh then H0 fallback, no chase loop
VIEWPORT_HEAD_READ_MAX_OBJECTS 1/request Cold/head/manual page cost; legacy256 walk elsewhere unchanged
VIEWPORT_RESPONSE_MAX_BYTES 2MiB full serialized snapshot/JSON Below actual4.5MB Function ceiling; not total live SSE lifetime bytes
LOCAL_VIEWPORT_CACHE_MAX_BYTES 1MiB UTF-8 JSON Disposable latest cache; UTF16 storage~2MiB worst case, quota may be lower
VIEWPORT_CACHE_MAX_BYTES 4MiB serialized/two windows Latest+viewed, JS/temp decode overhead acknowledged
Existing ring/page/row 2048/512/262144B Reused, display excerpts explicit
queueMirror Existing16×5000 chars in existing1MiB meta Explicit[]; JSON escaping worst~480KiB, combined meta can reject without clipping other fields
Existing Blob/meta/Function 8MiB/1MiB/2MiB Unchanged; no raw+parsed heap equivalence claim
Existing tool groups 200items/262144encodedB/229376detail budget No all-run tool map
Visited history objects Existing256 guard IDs only/manual traversal, no body cache
New bridge ordinal transport u32 maximum Per-object raw scalar, not global exact history count
Existing cursor/poll/turn/route 1e9/1000ms/1h+300000ms wrap+512steps/1800s route Unchanged
Concurrency One browser view demand/epoch; one head+sample; initial/final probes only; one metadata channel Finite tasks, cancel/late-result guards

Security, UI and cloud operations

  • Authentication/current-run ownership/session-bound object reads stay fail-closed. No client URL, secrets, arbitrary meta or historical run inputs. Restore cannot publish transcript rows or execute/start/cancel a model/tool.
  • Wasm product only; DOM host I/O. TEAL chrome, WARM accents, EMBER real errors via existing palette. Missing history neutral System information.
  • Composer/Send/Stop/status geometry stable under loading/retry. Draft/focus/FIFO preserved, desktop/~390px usable. No app GPA/network in ui.frame.
  • No admin migration/backfill/secret/config cutover. Existing Git deploy/int-durable/current Wasm; native phase through configured build-harness→matched PR/main artifact→Vercel. No laptop ritual, setup nag, SDK enable toggle or new service.
  • Rollback code without deleting canonical history. Old workers remain old code; cross-version metadata losses acceptable, not “solved” by claims of atomic whole history.

Mandatory tests / DoD

All shipping phases: npm test, npm run test:int, npm run typecheck, npm run build; real current Wasm fail-closed, no skip/expected-failure mask on delivered contract. No tests run while planning.

Case Evidence
#924 recent viewport Production controller/client/parser/apply→real Wasm ring; no prefix wipe/replay
#924 thinking No pre-H sampled reasoning payload/paint; post-H thinking appears
#924 Busy/Stop running/cancelling during delayed recovery, server H, explicit cancel works, no second POST
Cost defaults Lazy huge history/stream, finite calls/frame/byte/time budgets, one final probe, no head prev reads/catch-up loop
Legacy ambiguity Missing prompt/old segments/repeated text/partial tools gives marked best-effort view or cached fallback, not precision blocker
Write separation Zero partial transcript mint/upload/PUT; metadata field patch rejects messages/pointers and preserves current record fields on race/delete
Queue absent/invalid/[], duplicate text, strip-before-send once, failed-start restore, desired-array response-loss/coalescing; no exactly-once journal
Cache Large v1→v2→new heap→actual controller/ring; no steady full stringify; failed v2 leaves v1; no cache full PUT
History Tail-only host actual scoped page→ring, raw cursor progress on duplicates/errors, one-object read, bounded windows/anchors/latest/live separation
Edge/visual Stop/drop/terminal/New/Clear/switch/late results/denied storage, raw/synthetic indices; matched artifact/protocol + hosted desktop/mobile geometry smoke

Living docs

Surface Changes / owner
docs/session-model.md Best-effort view/omissions, partial-write separation, desired metadata queue, cache migration, per-object pages; relevant phase
docs/agent-stream.md Negotiation/index/snapshot/handoff and live thinking; #959/#960
docs/harness-limits.md New budgets + omissions + SDK/memory residuals; relevant phase
docs/feature-divide.md Host I/O/canvas lifecycle/navigation, no dual UI; #960/#962
AGENTS.md Helper/store/bridge ownership and pragmatic bounded-work rule/real-Wasm gate
README.md Concise shipped fast reload/cache/history + guide links; #960–#962
SECURITY.md Scoped reader and allowlisted field-only PATCH, no source replacement; #959/#960
native/harness/README.md Actual navigation exports/source coordinates and next paired version; #962
.env.example N/A no new secret/env/feature flag

Guides timeless, no phase/issue process narrative as primary explanation.

Open questions and accepted residuals

No design blockers. Do not recreate the removed exactness/archive/certificate/transaction subsystems during implementation. Choose the written sensible fallback within these bounds; new security flaws or actual transport violations still must be fixed.

Accepted: older rows omitted, recent sample/gap skipped, fragments/duplicates, approximate snapshot-local seek/anchors, cache-only history loss beyond latest window, existing worker and cross-tab preference/queue LWW races. Merge gates prove bounded work and better usability—not perfect historical equality.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions