Skip to content

fix(tui): cache settled alt-screen transcript#647

Open
PierrunoYT wants to merge 1 commit into
Gitlawb:mainfrom
PierrunoYT:fix/issue-561-transcript-render-cache
Open

fix(tui): cache settled alt-screen transcript#647
PierrunoYT wants to merge 1 commit into
Gitlawb:mainfrom
PierrunoYT:fix/issue-561-transcript-render-cache

Conversation

@PierrunoYT

@PierrunoYT PierrunoYT commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • advance the settled transcript frontier in alt-screen mode without emitting tea.Println
  • cache settled transcript body items so fullscreen history remains scrollable without rebuilding it every frame
  • build row context from only the live tail and preserve dynamic hover, selection, resize, and expand/collapse behavior
  • add regression coverage and a 5,000-turn benchmark for the settled alt-screen path

Root cause

Alt-screen mode returned from settleTranscript before advancing m.flushed. Since the shipped TUI always uses alt-screen mode, every redraw rebuilt transcript body descriptors and cross-row context for the complete conversation.

Impact

Settled alt-screen frames now reuse cached body descriptors while the live tail remains dynamic. On the issue benchmark, a settled 5,000-turn transcript takes about 110 ns/op with 0 B/op and 0 allocations/op on Windows/amd64.

Validation

  • focused frontier, transcript selection, hover, and rendering tests
  • go test ./... -run '^$'
  • go test ./internal/tui -run '^$' -bench '^BenchmarkIssue561SettledAltScreen$' -benchmem -benchtime=100x

Closes #561

Summary by CodeRabbit

  • Bug Fixes

    • Improved transcript rendering in alternate-screen mode so settled content remains visible while new content is processed.
    • Ensured expanding or collapsing transcript sections immediately updates the displayed content.
    • Improved rendering consistency when transcript content changes or the display width is adjusted.
  • Performance

    • Reduced unnecessary transcript rebuilding and improved rendering efficiency for settled content.

@PierrunoYT PierrunoYT marked this pull request as ready for review July 10, 2026 16:59
Copilot AI review requested due to automatic review settings July 10, 2026 16:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the alt-screen flush frontier behavior and introduces a settled-prefix cache so Bubble Tea’s fullscreen (alt-screen) TUI no longer rebuilds transcript body descriptors and cross-row context for the entire history on every redraw. It advances m.flushed in alt-screen mode (while still skipping tea.Println) and reuses cached settled transcript items while keeping the live tail dynamic for hover/selection and streaming state.

Changes:

  • Advance the flush frontier in alt-screen mode and cache the settled transcript prefix as []transcriptBodyItem to avoid per-frame O(history) rebuilds.
  • Build rowContext only from the unflushed tail and preserve spacing context via flushedPreviousKind / flushedHavePreviousKind.
  • Add regression tests and a benchmark for the settled alt-screen path, plus a small allocation avoidance in buildRowContext for the frontier-at-tail steady state.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
internal/tui/transcript_selection.go Adds settled-item reuse path for alt-screen, plus interaction state plumbing so cached item render closures reflect current hover/selection without rebuilding descriptors.
internal/tui/transcript_issue561_bench_test.go Adds a regression benchmark to measure settled alt-screen transcript body item construction at large transcript sizes.
internal/tui/rendering.go Avoids per-frame map allocations by returning an empty rowContext when called with an empty tail slice.
internal/tui/model.go Extends model state to track flush spacing context and store alt-screen settled cache metadata and shared interaction pointer.
internal/tui/flush.go Removes the alt-screen early return; advances the frontier in alt-screen while skipping printing, caches settled prefix items, and narrows buildRowContext to the tail.
internal/tui/flush_test.go Updates alt-screen flush behavior expectations and adds a regression test ensuring settled cache rebuild after expand/collapse toggles.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Alt-screen transcript settlement now advances the flush frontier, skips native scrollback emission, and caches settled body items. Rendering reuses the cache and incremental row context, with invalidation for row toggles and coverage through tests and benchmarks.

Changes

Alt-screen transcript rendering

Layer / File(s) Summary
Frontier advancement and settled cache
internal/tui/model.go, internal/tui/flush.go
Alt-screen settlement advances frontier bookkeeping, avoids tea.Println, and caches settled transcript items by width and frontier.
Incremental transcript rendering
internal/tui/transcript_selection.go, internal/tui/rendering.go
Rendering shares selection and hover state safely, processes the relevant transcript tail, reuses settled items, and invalidates the cache when settled rows change.
Alt-screen behavior coverage
internal/tui/flush_test.go, internal/tui/transcript_issue561_bench_test.go
Tests verify frontier advancement and cache rebuilding; a benchmark measures repeated settled alt-screen rendering.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant settleTranscript
  participant rebuildAltScreenSettledItems
  participant transcriptBodyItems
  settleTranscript->>rebuildAltScreenSettledItems: advance flushed frontier
  rebuildAltScreenSettledItems->>transcriptBodyItems: rebuild settled prefix descriptors
  transcriptBodyItems-->>rebuildAltScreenSettledItems: settled body items
  rebuildAltScreenSettledItems-->>settleTranscript: store width and frontier cache
Loading

Possibly related PRs

  • Gitlawb/zero#300: Overlaps with alt-screen settled-row caching and transcript body-item construction.
  • Gitlawb/zero#350: Overlaps with resetFlushFrontier() and related transcript state reset logic.
  • Gitlawb/zero#498: Intersects with transcript body-item rendering and detailed-mode selection handling.

Suggested reviewers: gnanam1990, Vasanthdev2004, jatmn, kevincodex1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes advance the alt-screen flush frontier, limit scanning to the live tail, and add caching/benchmarks as requested by #561.
Out of Scope Changes check ✅ Passed The added interaction plumbing, cache rebuild logic, tests, and benchmark all support the alt-screen transcript perf fix.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: caching settled transcript data in alt-screen TUI rendering.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/tui/transcript_issue561_bench_test.go (1)

21-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: guard against a silent cache miss so a future regression doesn't quietly turn this into an O(history) benchmark. A cheap pre-check (e.g. assert m.altScreenSettledFrontier == m.flushed && m.altScreenSettledWidth == width before the loop) keeps the benchmark measuring the intended fast path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/transcript_issue561_bench_test.go` around lines 21 - 29, Ensure
BenchmarkIssue561SettledAltScreen verifies the settled cache state before
timing: assert that m.altScreenSettledFrontier equals m.flushed and
m.altScreenSettledWidth equals width after setup and before the benchmark loop,
so cache misses cannot silently change the benchmark to O(history).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/tui/transcript_issue561_bench_test.go`:
- Around line 21-29: Ensure BenchmarkIssue561SettledAltScreen verifies the
settled cache state before timing: assert that m.altScreenSettledFrontier equals
m.flushed and m.altScreenSettledWidth equals width after setup and before the
benchmark loop, so cache misses cannot silently change the benchmark to
O(history).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7bb8825d-3566-40bc-8e2b-74ddc6b163df

📥 Commits

Reviewing files that changed from the base of the PR and between 1af5882 and ca44988.

📒 Files selected for processing (6)
  • internal/tui/flush.go
  • internal/tui/flush_test.go
  • internal/tui/model.go
  • internal/tui/rendering.go
  • internal/tui/transcript_issue561_bench_test.go
  • internal/tui/transcript_selection.go

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I built and vetted this and read all six changed files, and the caching design is sound invalidation covers new turns, resize, and expand/collapse of settled rows, and the steady-state fast path correctly bypasses the cache whenever a row is still live. The benchmark matches the stated goal.

But I found a regression that I think has to be fixed before this lands: in alt-screen mode the doctor connectivity spinner freezes.

setDoctorStatusRow (command_center.go:212-222) updates an existing status row in place with m.transcript[i] = row and returns. A rowSystem row is immediately settleable (flush.go:61-64), so once the frontier advances past the doctor row it gets snapshotted into altScreenSettledItems. On the next spinner tick (model.go:1986) doctorFrame changes and setDoctorStatusRow writes new text into m.transcript[i] but it never zeroes altScreenSettledWidth, and the rebuild guard in settleTranscript (flush.go:128) only fires on a frontier/width change. With none of those changing on an in-place text update, the rebuild is skipped and the View fast path (transcript_selection.go:249) serves the stale snapshot. I reproduced it: advancing doctorFrame 0 → 2 still renders the frame-0 glyph. The same repro passes on origin/main, where alt-screen re-rendered the tail every frame.

Fix is small: have setDoctorStatusRow invalidate the settled cache when it updates an existing row (zero altScreenSettledWidth on the in-place path, same as toggleTranscriptRow does). Worth checking other in-place mutators of already-settled rows for the same gap while you're there.

Everything else I'd merge as-is. Requesting changes just for this.

@Vasanthdev2004

Copy link
Copy Markdown
Collaborator

@gnanam1990 could you take a look here? I requested changes for a regression: in alt-screen mode the doctor connectivity spinner freezes, because setDoctorStatusRow updates a settled row in place without invalidating the settled-prefix cache, so the View fast path serves a stale snapshot. Would be good to have you confirm (or push back) before it's reworked.

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.

perf(tui): transcript re-renders full history every frame — flush frontier never advances in alt-screen

3 participants