Skip to content

feat(terminal): show auto-compaction indicator - #111

Merged
omarluq merged 5 commits into
mainfrom
feat/auto-compaction-indicator
Jun 12, 2026
Merged

feat(terminal): show auto-compaction indicator#111
omarluq merged 5 commits into
mainfrom
feat/auto-compaction-indicator

Conversation

@omarluq

@omarluq omarluq commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • emit explicit context compaction start/done/error stream events for auto compaction paths
  • map auto compaction lifecycle events to the existing terminal compaction indicator state
  • keep existing transcript notices while showing the same status/shimmer behavior as manual compaction

Validation

  • go test ./internal/assistant ./internal/terminal
  • golangci-lint run ./internal/assistant ./internal/terminal
  • task ci

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@omarluq, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 3 hours, 25 minutes, and 21 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d730de6b-2f79-44d9-bff5-31c9cc1813cb

📥 Commits

Reviewing files that changed from the base of the PR and between 1e653e7 and e9a6bbf.

📒 Files selected for processing (3)
  • internal/terminal/compact_commands_test.go
  • internal/terminal/panel_settings.go
  • internal/terminal/prompt_send_test.go
📝 Walkthrough

Walkthrough

This PR replaces a single generic compaction stream event with three lifecycle-scoped events (start, done, error) and updates assistant emitters, overflow recovery, client/persistence mapping, terminal async routing and UI state, plus tests to assert the new lifecycle semantics.

Changes

Context compaction lifecycle events

Layer / File(s) Summary
Stream event kind definitions
internal/assistant/runtime.go
Added StreamEventContextCompactionStart, StreamEventContextCompactionDone, and StreamEventContextCompactionError constants.
Auto-compaction lifecycle events
internal/assistant/context_auto_compaction.go
Introduced compactBeforeRequest and emitContextCompactionEvent/emitContextCompactionError helpers; emits start/done/error for pre-request and post-response compaction.
Overflow recovery compaction events
internal/assistant/context_overflow_compaction.go
Moved compaction into compactAfterProviderOverflow and emit start/done/error events around recovery compaction and validation.
Client adapter & persistence mapping
internal/assistant/client_adapter.go, internal/assistant/runtime_persist.go
Recognize new compaction lifecycle event kinds in stream→LLM mapping and persistence ignore-list.
Assistant test updates
internal/assistant/context_auto_compaction_test.go, internal/assistant/context_auto_compaction_internal_extra_test.go, internal/assistant/context_overflow_compaction_test.go, internal/assistant/context_post_response_auto_compaction_test.go
Updated predicates and assertions to accept and validate start/done/error lifecycle event kinds.
Terminal async event system
internal/terminal/async_events.go
Added asyncEventCompactStart, asyncContextEventKind mapper, applyPromptContextEvent, and compaction indicator helpers to route lifecycle events into UI state changes.
Terminal UI & commands
internal/terminal/app.go, internal/terminal/compact_commands.go
Classified compact start as high-volume (affects redraw) and guard done/error handling on activeCompaction presence.
Terminal tests
internal/terminal/async_events_test.go, internal/terminal/compact_commands_test.go
Refactored test case generation to include compaction lifecycle cases and added lifecycle assertions for app.compacting and status messages; added test ensuring auto-compaction events pass through manual-compact handlers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • omarluq/librecode#83: Earlier context compaction event plumbing that this PR refactors from generic to lifecycle-based events.
  • omarluq/librecode#87: Related changes around overflow recovery compaction emission and handling.
  • omarluq/librecode#95: Overlaps on auto/overflow compaction codepaths and lifecycle event reporting.

"I nibbled through the log with care,
Start and Done now hop on air,
If errors bloom, I squeak them loud,
Compaction tracked, the devs are proud.
🥕✨ — your rabbit reviewer"

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(terminal): show auto-compaction indicator' accurately summarizes the main change: adding a visual indicator for auto-compaction in the terminal UI.
Description check ✅ Passed The description clearly outlines the three key changes: emitting context compaction lifecycle events, mapping them to terminal state, and preserving transcript notices while showing compaction status.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/auto-compaction-indicator

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

@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.15789% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.45%. Comparing base (2a92ed9) to head (e9a6bbf).

Files with missing lines Patch % Lines
internal/assistant/context_auto_compaction.go 86.20% 7 Missing and 1 partial ⚠️
internal/assistant/context_overflow_compaction.go 87.50% 6 Missing ⚠️
internal/terminal/async_events.go 90.24% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #111      +/-   ##
==========================================
+ Coverage   77.41%   77.45%   +0.03%     
==========================================
  Files         260      260              
  Lines       21377    21484     +107     
==========================================
+ Hits        16550    16640      +90     
- Misses       3619     3635      +16     
- Partials     1208     1209       +1     
Flag Coverage Δ
unittests 77.45% <88.15%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/terminal/compact_commands.go (1)

94-121: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Auto-compaction done/error events are silently dropped.

handleCompactAsyncEvent intercepts asyncEventCompactDone and asyncEventCompactError (lines 96-101) and returns true (handled), but then applyCompactDone and applyCompactError immediately return early via ignoreCompactEvent when activeCompaction is nil (line 152).

For auto-compaction flows (where activeCompaction is never set), this prevents the events from reaching handlePromptAsyncEventapplyPromptContextEvent in async_events.go:341-373, which is the intended handler for auto-compaction lifecycle events. As a result, the UI never updates app.compacting, status messages, or transcript notices for auto-compaction.

The asymmetry confirms the bug: asyncEventCompactStart returns false at line 105 (allowing it to reach applyPromptContextEvent), but done/error return true, blocking them.

Fix: Check ignoreCompactEvent before returning true, allowing auto-compaction events to fall through to the prompt handler:

🐛 Proposed fix
 func (app *App) handleCompactAsyncEvent(ctx context.Context, payload *asyncEvent) bool {
 	switch payload.Kind {
 	case asyncEventCompactDone:
+		if app.ignoreCompactEvent(payload) {
+			return false
+		}
 		app.applyCompactDone(ctx, payload)
 		return true
 	case asyncEventCompactError:
+		if app.ignoreCompactEvent(payload) {
+			return false
+		}
 		app.applyCompactError(payload)
 		return true
🤖 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/terminal/compact_commands.go` around lines 94 - 121,
handleCompactAsyncEvent currently unconditionally claims asyncEventCompactDone
and asyncEventCompactError as handled which blocks auto-compaction flows; update
handleCompactAsyncEvent to check app.ignoreCompactEvent(payload) first and if it
returns true return false so the event falls through to the prompt handler,
otherwise call applyCompactDone/applyCompactError and return true; reference the
functions handleCompactAsyncEvent, ignoreCompactEvent, applyCompactDone and
applyCompactError when making the change.
🧹 Nitpick comments (1)
internal/terminal/async_events_test.go (1)

435-446: ⚡ Quick win

Consider adding a test case for empty-text compaction done events.

The "prompt context done" test always passes non-empty text (asyncTestCompact at line 436), but according to internal/terminal/async_events.go lines 349-351, the done handler only sets app.statusMessage when payload.Text != "". The edge case where a done event arrives with empty text is not validated.

✅ Proposed test case for empty-text done event

Add this case to promptLifecycleEventCases() after the existing "prompt context done" case:

 		{
 			name:    "prompt context done",
 			payload: asyncTestEvent(asyncEventCompactDone, "", asyncTestCompact, 1),
 			setup: func(app *App) {
 				app.compacting = true
 			},
 			assert: func(t *testing.T, app *App) {
 				t.Helper()
 				assert.False(t, app.compacting)
 				assert.Equal(t, compactedStatusMessage, app.statusMessage)
 			},
 			wantHandled: true,
 		},
+		{
+			name:    "prompt context done with empty text",
+			payload: asyncTestEvent(asyncEventCompactDone, "", "", 1),
+			setup: func(app *App) {
+				app.compacting = true
+				app.statusMessage = "previous status"
+			},
+			assert: func(t *testing.T, app *App) {
+				t.Helper()
+				assert.False(t, app.compacting)
+				assert.Equal(t, "previous status", app.statusMessage, "status should not change when text is empty")
+			},
+			wantHandled: true,
+		},
 		{
 			name:    "prompt retry",
🤖 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/terminal/async_events_test.go` around lines 435 - 446, Add a test
case in promptLifecycleEventCases() that sends an
asyncTestEvent(asyncEventCompactDone, "", asyncTestCompact, 1) with an empty
payload.Text and app.compacting = true in setup, then assert that app.compacting
becomes false but app.statusMessage is NOT set to compactedStatusMessage (i.e.,
remains unchanged); this verifies the handler in async_events.go only updates
statusMessage when payload.Text != "" and covers the empty-text edge case.
🤖 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.

Inline comments:
In `@internal/terminal/async_events_test.go`:
- Around line 422-446: Add a new test case to promptLifecycleEventCases that
validates the error path for compaction: create a case named "prompt context
error" with payload asyncTestEvent(asyncEventCompactError, "", asyncTestCompact,
1), a setup that sets app.compacting = true, and an assert that checks
assert.False(t, app.compacting) and assert.Equal(t, compactedStatusMessage,
app.statusMessage); set wantHandled: true so applyPromptContextEvent handling of
asyncEventCompactError is verified.

---

Outside diff comments:
In `@internal/terminal/compact_commands.go`:
- Around line 94-121: handleCompactAsyncEvent currently unconditionally claims
asyncEventCompactDone and asyncEventCompactError as handled which blocks
auto-compaction flows; update handleCompactAsyncEvent to check
app.ignoreCompactEvent(payload) first and if it returns true return false so the
event falls through to the prompt handler, otherwise call
applyCompactDone/applyCompactError and return true; reference the functions
handleCompactAsyncEvent, ignoreCompactEvent, applyCompactDone and
applyCompactError when making the change.

---

Nitpick comments:
In `@internal/terminal/async_events_test.go`:
- Around line 435-446: Add a test case in promptLifecycleEventCases() that sends
an asyncTestEvent(asyncEventCompactDone, "", asyncTestCompact, 1) with an empty
payload.Text and app.compacting = true in setup, then assert that app.compacting
becomes false but app.statusMessage is NOT set to compactedStatusMessage (i.e.,
remains unchanged); this verifies the handler in async_events.go only updates
statusMessage when payload.Text != "" and covers the empty-text edge case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f649c159-aeff-44d7-9ec6-b247af7b9ed6

📥 Commits

Reviewing files that changed from the base of the PR and between 2a92ed9 and 5aec7aa.

📒 Files selected for processing (13)
  • internal/assistant/client_adapter.go
  • internal/assistant/context_auto_compaction.go
  • internal/assistant/context_auto_compaction_internal_extra_test.go
  • internal/assistant/context_auto_compaction_test.go
  • internal/assistant/context_overflow_compaction.go
  • internal/assistant/context_overflow_compaction_test.go
  • internal/assistant/context_post_response_auto_compaction_test.go
  • internal/assistant/runtime.go
  • internal/assistant/runtime_persist.go
  • internal/terminal/app.go
  • internal/terminal/async_events.go
  • internal/terminal/async_events_test.go
  • internal/terminal/compact_commands.go

Comment thread internal/terminal/async_events_test.go
@omarluq
omarluq force-pushed the feat/auto-compaction-indicator branch from 5aec7aa to 5523226 Compare June 12, 2026 02:49

@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.

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@internal/assistant/context_auto_compaction.go`:
- Around line 172-177: Move the runtime.emitContextCompactionEvent call with
StreamEventContextCompactionDone/autoCompactionMessage so it runs only after the
rebuilt request has been reconstructed and the budget re-validation succeeds;
specifically, remove the early emit before the rebuild/validation block and
place it after the rebuild (the code that constructs the rebuilt request) and
after the call that re-validates the budget (the validate/revalidateBudget
logic), mirroring the overflow path so the "done" event is emitted only on
successful rebuild+validation.

In `@internal/assistant/context_overflow_compaction.go`:
- Around line 104-108: The done event is reporting the post-compaction budget
(recoveredBuild.Budget) but should report the original pre-compaction estimated
budget; update the call to runtime.emitContextCompactionEvent that builds the
compaction message to pass input.build.Budget instead of recoveredBuild.Budget
so compactionMessage("context auto-compacted after provider overflow",
input.build.Budget, recoveredEntry) is used (same
runtime.emitContextCompactionEvent and parameters: ctx,
input.preparation.onEvent, StreamEventContextCompactionDone,
compactionMessage(...)).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f9c45ef-75f7-40ef-b196-e6ebe98c180e

📥 Commits

Reviewing files that changed from the base of the PR and between 5aec7aa and 5523226.

📒 Files selected for processing (13)
  • internal/assistant/client_adapter.go
  • internal/assistant/context_auto_compaction.go
  • internal/assistant/context_auto_compaction_internal_extra_test.go
  • internal/assistant/context_auto_compaction_test.go
  • internal/assistant/context_overflow_compaction.go
  • internal/assistant/context_overflow_compaction_test.go
  • internal/assistant/context_post_response_auto_compaction_test.go
  • internal/assistant/runtime.go
  • internal/assistant/runtime_persist.go
  • internal/terminal/app.go
  • internal/terminal/async_events.go
  • internal/terminal/async_events_test.go
  • internal/terminal/compact_commands.go
✅ Files skipped from review due to trivial changes (1)
  • internal/assistant/client_adapter.go
🚧 Files skipped from review as they are similar to previous changes (8)
  • internal/terminal/app.go
  • internal/assistant/runtime.go
  • internal/assistant/context_auto_compaction_internal_extra_test.go
  • internal/assistant/runtime_persist.go
  • internal/assistant/context_post_response_auto_compaction_test.go
  • internal/assistant/context_auto_compaction_test.go
  • internal/assistant/context_overflow_compaction_test.go
  • internal/terminal/async_events.go

Comment thread internal/assistant/context_auto_compaction.go Outdated
Comment thread internal/assistant/context_overflow_compaction.go Outdated

@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/terminal/compact_commands_test.go (1)

529-529: 💤 Low value

Consider renaming test case for consistency.

The test case name "completion event" breaks the naming pattern established by the other cases ("start" and "error"). Consider renaming it to "done" to match the event kind suffix and maintain consistency.

✨ Proposed naming fix
-		{name: "completion event", kind: asyncEventCompactDone},
+		{name: "done", kind: asyncEventCompactDone},
🤖 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/terminal/compact_commands_test.go` at line 529, Rename the test case
string "completion event" to match the existing naming pattern by using "done"
so it aligns with the event kind suffix; update the test table entry where the
case is defined (the entry with name: "completion event", kind:
asyncEventCompactDone) to name: "done" to keep consistency with the other cases
("start" and "error").
🤖 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/terminal/compact_commands_test.go`:
- Line 529: Rename the test case string "completion event" to match the existing
naming pattern by using "done" so it aligns with the event kind suffix; update
the test table entry where the case is defined (the entry with name: "completion
event", kind: asyncEventCompactDone) to name: "done" to keep consistency with
the other cases ("start" and "error").

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0b9654be-7daa-4082-b717-eba51087b001

📥 Commits

Reviewing files that changed from the base of the PR and between 5523226 and 1e653e7.

📒 Files selected for processing (4)
  • internal/assistant/context_auto_compaction.go
  • internal/assistant/context_overflow_compaction.go
  • internal/terminal/async_events_test.go
  • internal/terminal/compact_commands_test.go

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 12, 2026
@sonarqubecloud

Copy link
Copy Markdown

@omarluq
omarluq merged commit 4de3adc into main Jun 12, 2026
15 checks passed
@omarluq
omarluq deleted the feat/auto-compaction-indicator branch June 12, 2026 03:47
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