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
add revision-aware invalidation and render caching to the TUI component tree
reuse stable transcript prefixes and fullscreen scrollbar projections
wire interactive messages, tool execution, streaming, and spinner updates into the invalidation path
add regression coverage for stable renders, tail-only updates, nested containers, duplicate children, width changes, and fullscreen scrolling
Root cause
Long sessions rebuilt and flattened every historical transcript line on each render. Fullscreen ScrollView then transformed the complete child output for scrollbar styling before clipping it to the viewport. OMO tool streaming and spinner refreshes repeat that work frequently, making each frame O(total rendered history) and causing progressively higher CPU, allocation, and GC pressure.
This is separate from c93e786, which bounds persisted apply_patch result payloads. This branch is based on current main, which already contains that fix.
Cache behavior
The change is confined to terminal rendering and does not alter model request construction. A hermetic baseline-versus-fixed capture produced byte-identical Responses API requests:
This preserves Senpi's provider-side prompt-cache hit behavior.
Stress validation
A/B replay used the same 41,082,566-byte, 6,321-entry real Senpi session snapshot against exact upstream main (35af9748b) and this commit, in fullscreen mode at 120x34 with alternating input for about 15.5 seconds:
Metric
Upstream main
This PR
Change
One-core CPU
77.38%
7.94%
-89.78%
Peak working set
1,136.83 MiB
1,011.42 MiB
-125.41 MiB
Peak private memory
1,153.48 MiB
1,065.33 MiB
-88.15 MiB
Both cases ran in isolated offline sandboxes and their spawned Senpi processes were terminated after measurement.
focused interactive rendering suite (10 files, 102 tests passed)
cache integrity suite (6 files, 45 tests passed)
Biome check on all 16 changed files
request-equivalence harness (raw request and all cache-relevant fields identical)
real-session A/B stress harness
The broader TUI suite was also compared before publication: both baseline and fixed runs had 81 passing and the same 7 Windows-environment failures (WSL /bin/bash, chmod semantics, crash-dump environment, and path separators).
Summary by cubic
Add revision-aware render caching with shared-child invalidation to the tui component tree so long sessions don’t re-render history. Scrolling and streaming now update only the changed tail, making fullscreen sessions smooth and much faster.
Bug Fixes
Cache stable prefixes and fullscreen scrollbar projections across Container, ScrollView, Text, Markdown, Spacer, Box, and DynamicBorder.
Add revision and invalidation APIs (getRenderRevision, getRenderChangeStart, setRenderInvalidationCallback, isRenderCacheTrackable) and binding‑aware Container mutations (insertChild, replaceChild, detachChildrenFrom); children is now read‑only. Invalidation now fans out to every parent for shared children and supports repeated references; coding‑agent paths were updated to use these calls.
Wire invalidation through interactive pieces: assistant/user/custom messages, tool execution, loader, and spinners.
Results: ~-90% one-core CPU and lower peak memory in a 6k-entry session; tests cover cache integrity, tail-only updates, shared-child fan-out, repeated references, width changes, and fullscreen scrollbar projection.
Written for commit d13301a. Summary will update on new commits.
Thanks — the shared-child concern was real. This TUI does legitimately project one component through multiple containers, so I implemented fan-out instead of asserting a strict tree:
one dispatcher occupies the component's callback slot and notifies every containing parent
duplicate references inside one parent remain reference-counted
detaching from one parent removes only that parent's subscription
the new regression test mutates a shared child, verifies both parents refresh, detaches one parent, then verifies only the remaining parent refreshes
I also closed the mutable-array footgun: Container.children is now a read-only view backed by private storage, and mutations go through the binding-aware methods. I grep-audited direct mutations and converted the live TUI-container cases in test/chat-simple.ts; remaining matches are unrelated layout/data-tree models or comments.
The smaller notes are covered too: Box now documents why bgFn("test") is only a best-effort compatibility probe, and a no-change render resets changeStart to the end of the cached output.
Validation on d13301a92:
render-cache/layout tests: 19 passed, including 8 focused cache regressions
hermetic Responses request SHA remains 32ca2f7408d059d139692de92c4297dce880239a0ee39bf03f785267dbd38b6d; request fields including prompt_cache_key are unchanged
the 41,082,566-byte / 6,321-entry real session was replayed again with the native Windows ConPTY backend for 150 interactions; the source session and auth files remained unchanged
The PR body now includes the implementation details and refreshed validation evidence.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Root cause
Long sessions rebuilt and flattened every historical transcript line on each render. Fullscreen
ScrollViewthen transformed the complete child output for scrollbar styling before clipping it to the viewport. OMO tool streaming and spinner refreshes repeat that work frequently, making each frame O(total rendered history) and causing progressively higher CPU, allocation, and GC pressure.This is separate from c93e786, which bounds persisted
apply_patchresult payloads. This branch is based on currentmain, which already contains that fix.Cache behavior
The change is confined to terminal rendering and does not alter model request construction. A hermetic baseline-versus-fixed capture produced byte-identical Responses API requests:
32ca2f7408d059d139692de92c4297dce880239a0ee39bf03f785267dbd38b6dprompt_cache_keyThis preserves Senpi's provider-side prompt-cache hit behavior.
Stress validation
A/B replay used the same 41,082,566-byte, 6,321-entry real Senpi session snapshot against exact upstream
main(35af9748b) and this commit, in fullscreen mode at 120x34 with alternating input for about 15.5 seconds:Both cases ran in isolated offline sandboxes and their spawned Senpi processes were terminated after measurement.
Validation
npm ci --ignore-scriptsnpm run buildnode --import tsx --test packages/tui/test/container-render-cache.test.ts(6 passed)The broader TUI suite was also compared before publication: both baseline and fixed runs had 81 passing and the same 7 Windows-environment failures (WSL
/bin/bash, chmod semantics, crash-dump environment, and path separators).Summary by cubic
Add revision-aware render caching with shared-child invalidation to the
tuicomponent tree so long sessions don’t re-render history. Scrolling and streaming now update only the changed tail, making fullscreen sessions smooth and much faster.Container,ScrollView,Text,Markdown,Spacer,Box, andDynamicBorder.getRenderRevision,getRenderChangeStart,setRenderInvalidationCallback,isRenderCacheTrackable) and binding‑awareContainermutations (insertChild,replaceChild,detachChildrenFrom);childrenis now read‑only. Invalidation now fans out to every parent for shared children and supports repeated references; coding‑agent paths were updated to use these calls.Written for commit d13301a. Summary will update on new commits.