Skip to content

fix(engine,web): F1 bound every terminal replay (WI-125) - #668

Closed
thedancingdeveloper wants to merge 1 commit into
feat/terminal-attach-h1-harnessfrom
feat/terminal-attach-f1-bound-replay
Closed

fix(engine,web): F1 bound every terminal replay (WI-125)#668
thedancingdeveloper wants to merge 1 commit into
feat/terminal-attach-h1-harnessfrom
feat/terminal-attach-f1-bound-replay

Conversation

@thedancingdeveloper

Copy link
Copy Markdown
Contributor

Second PR of the terminal-attach-budget initiative (WI-121). Stacked on #667 (H1) — its base is feat/terminal-attach-h1-harness, so this diff shows only F1's changes; both land on feat/terminal-attach-budget, nothing to main until H3 is green. Merge #667 first (or merge this into the feature branch after it).

The bug (WI-125)

When a warm reattach's resume cursor had aged out of the ring (a parked session produced more than the 4 MiB ring while away, or the cache was stale), snapshot_for_attach returned the whole untrimmed ring with reset:true — up to 4 MiB parsed into an xterm that keeps 5000 lines, 4× worse than a cold attach (capped at 1 MiB), and painted above a screen the client already had stale. An oversized in-window delta had the same problem.

The fix

Engine (pty.rs)snapshot_tail_bytes is now the replay budget on every path:

  • warm, cursor retained, delta ≤ budget → byte-exact delta, reset:false (an ordinary switch-away/switch-back still appends);
  • warm, cursor aged out or delta > budget → ground-state-aligned tail ≤ budget, reset:true;
  • cold → bounded tail (unchanged);
  • in-band resync (tail_bytes=None) → unchanged.

Web (api.ts)openAttach now always sends snapshot_tail_bytes alongside resume_from. Terminal.tsx already resets + clears cache on any reset:true; the snapshot-start position arithmetic is extracted to snapshotStartPosition() and unit-tested, so the re-anchor after a reset-following-a-resume (discard stale cursor → re-anchor to scrollback_pos - scrollback_bytes → resume gap-free) is pinned.

Tests

  • H1 large_stale_warm_reattach_* and four_mib_ring_stale_reattach_* "before" flood assertions flipped to bounded (len ≤ budget, reset:true, ground-state aligned).
  • warm_attach_is_not_narrowed_by_a_tail_hint invertedwarm_attach_over_budget_is_a_bounded_reset; new warm_attach_within_budget_stays_a_byte_exact_delta keeps the reset:false byte-exact case.
  • web: new snapshotStartPosition.test.ts; attachAuthFrame.test.ts updated for the always-sent budget.

Verification

  • cargo test -p vogt-engine-server --test integration104 passed; cargo fmt --check + clippy --tests clean.
  • pnpm typecheck clean; pnpm vitest906 passed.
  • scripts/check_docs.py clean. docs/ENGINE.md §5 documents the budget contract (reset:true may follow a resume_from).

🤖 Generated with Claude Code

https://claude.ai/code/session_01UtEFLJAhLiq4NfZCN2vuBb

The stale warm reattach flood: when a client's resume cursor had aged out of
the scrollback ring, snapshot_for_attach returned the FULL untrimmed ring with
reset:true — up to 4 MiB parsed into an xterm that keeps 5000 lines, 4x worse
than a cold attach, and above a screen the client already had stale. An
oversized in-window delta had the same problem.

Engine (pty.rs): snapshot_tail_bytes is now the replay budget on every path.
A warm reattach whose cursor is retained and whose delta fits the budget is
still returned byte-exact with reset:false; a delta that aged out OR exceeds
the budget returns a ground-state-aligned tail of at most the budget with
reset:true. The in-band resync path (tail_bytes=None) is unchanged.

Web (api.ts): openAttach always sends snapshot_tail_bytes: REPLAY_TAIL_MAX_BYTES
alongside resume_from, so warm reattaches are bounded too. Terminal.tsx already
resets and clears its cache on any reset:true; the snapshot-start position
arithmetic is extracted into snapshotStartPosition() so the re-anchor after a
reset-following-a-resume is unit-tested (the client discards its stale cursor,
re-anchors to scrollback_pos - scrollback_bytes, and resumes gap-free).

Tests:
- H1 (a)/(b) "before" flood assertions flipped to bounded (len <= budget,
  reset:true, ground-state aligned).
- warm_attach_is_not_narrowed_by_a_tail_hint inverted to
  warm_attach_over_budget_is_a_bounded_reset; new
  warm_attach_within_budget_stays_a_byte_exact_delta keeps the reset:false
  byte-exact case.
- web: snapshotStartPosition.test.ts; attachAuthFrame.test.ts updated for the
  always-sent budget.

Verification: cargo test -p vogt-engine-server --test integration 104 passed;
fmt + clippy clean; web typecheck clean; pnpm vitest 906 passed;
scripts/check_docs.py clean. docs/ENGINE.md §5 documents the budget contract.
WI-125.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UtEFLJAhLiq4NfZCN2vuBb
@thedancingdeveloper

Copy link
Copy Markdown
Contributor Author

Cleanup: superseded by the single promotion PR #676. All of this work is already integrated into feat/terminal-attach-budget and is reviewable there per-commit (each commit is one initiative item with a full message). Closing to keep the initiative to one PR; the commit is preserved.

@thedancingdeveloper
thedancingdeveloper deleted the feat/terminal-attach-f1-bound-replay branch September 9, 2026 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant