Skip to content

feat(assistant): add context seam breakdown#44

Merged
omarluq merged 3 commits into
mainfrom
feat/context-seam-debug
May 23, 2026
Merged

feat(assistant): add context seam breakdown#44
omarluq merged 3 commits into
mainfrom
feat/context-seam-debug

Conversation

@omarluq
Copy link
Copy Markdown
Owner

@omarluq omarluq commented May 23, 2026

Summary

  • adds bounded context_build extension contributions
  • tracks context breakdown for system, skills, history, and extensions
  • surfaces context breakdown through /context and token usage status

Validation

  • mise exec -- go test ./...
  • mise exec -- task build
  • mise exec -- task ci
  • cr review --agent -t uncommitted --base main

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a /context command to show a detailed token usage breakdown.
    • Token usage breakdown is now tracked and attached to requests.
  • Improvements

    • Context/token breakdowns are shown in terminal views and included in lifecycle/usage events.
    • Token usage merging preserves and surfaces detailed breakdown data.
  • Tests

    • Expanded and added tests to validate breakdown tracking, formatting, and usage helpers.

Walkthrough

This PR adds optional token usage breakdown tracking throughout the assistant and terminal modules. The TokenUsage struct gains a Breakdown map field; context building computes breakdown from system/skill/history tokens and extension contributions; the breakdown is wired through completion requests and emitted in lifecycle events; and the terminal displays it via a new /context slash command with safe merging logic.

Changes

Token Usage Breakdown Tracking

Layer / File(s) Summary
TokenUsage model and utility functions
internal/model/usage.go, internal/assistant/context_usage.go, internal/terminal/token_usage.go
TokenUsage struct adds optional Breakdown map[string]int field; EmptyTokenUsage() explicitly sets Breakdown: nil; helpers cloneIntMapForUsage and cloneTokenBreakdown provide defensive map copying.
Context build breakdown computation
internal/assistant/client.go, internal/assistant/context_build.go, internal/assistant/context_build_test.go
Added jsonBreakdownKey constant; initialContextBuildResult and recalculateContextBuildResult compute breakdown from system/skill/history/extension token counts; estimateContextBuildUsage accepts and populates breakdown in returned TokenUsage; tests validate non-nil breakdown.
Completion request integration
internal/assistant/runtime.go, internal/assistant/anthropic_internal_test.go
CompletionRequest adds Usage field; modelResponse passes contextResult.Usage into request; modelCompletionRequest signature extended to accept and assign usage parameter.
Usage event and lifecycle payload emission
internal/assistant/usage.go, internal/assistant/usage_events.go, internal/assistant/lifecycle.go
mergeUsage preserves reported breakdown when current is empty; emitUsage includes cloned breakdown in event payload; lifecycle payloads use jsonBreakdownKey to emit breakdown.
Terminal context command and display
internal/terminal/autocomplete.go, internal/terminal/commands.go, internal/terminal/context_commands.go
New /context slash command; handler receives original input text; showContextInfo displays token usage summary and breakdown lines; contextBreakdownLines formats sorted, filtered breakdown entries.
Terminal usage merging and safe breakdown handling
internal/terminal/token_usage.go, internal/terminal/token_usage_export_test.go, internal/terminal/app.go
mergeTerminalUsage clones next.Breakdown to avoid aliasing; App struct reordered to place tokenUsage before selection; test wrapper ContextBreakdownLinesForTest exposed.
Test fixture updates
internal/assistant/runtime_test.go, internal/assistant/runtime_events_test.go, internal/assistant/usage_test.go, internal/terminal/token_usage_test.go, internal/terminal/render_*.go
Assistant and terminal test files update TokenUsage struct literals to expanded form with explicit Breakdown: nil; terminal tests add breakdown key constants and validate preservation/formatting of breakdown during merge and display.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • omarluq/librecode#31: Introduces the context-build breakdown contract and JSON emission under jsonBreakdownKey; this PR extends that foundation to propagate breakdown through the request/event pipeline.
  • omarluq/librecode#10: Adds terminal token-usage status display; this PR extends the terminal usage pipeline with Breakdown field support and display via /context command.
  • omarluq/librecode#28: Modifies lifecycle payload construction and event emission; this PR aligns payload structure to include Breakdown in usage events and context-build payloads.

Poem

🐰 A rabbit hops through token streams,
Tracking breakdowns in assistant dreams—
Context counts by skill and task,
Now in /context, answers asked.
Maps are cloned with careful paws,
Breaking tokens without flaws! 🥕

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat(assistant): add context seam breakdown' clearly and concisely summarizes the main change—adding context breakdown tracking to the assistant module. The title is specific and directly reflects the primary objective of the changeset.
Description check ✅ Passed The PR description is directly related to the changeset, covering the main objectives: adding bounded context_build extension contributions, tracking context breakdown, and surfacing it through /context and token usage status. It includes validation steps.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/context-seam-debug

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

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 23, 2026

Codecov Report

❌ Patch coverage is 87.01299% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.68%. Comparing base (b3dd3a1) to head (11a1208).

Files with missing lines Patch % Lines
internal/terminal/commands.go 0.00% 3 Missing ⚠️
internal/terminal/context_commands.go 89.65% 2 Missing and 1 partial ⚠️
internal/assistant/context_usage.go 71.42% 1 Missing and 1 partial ⚠️
internal/assistant/usage.go 75.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #44      +/-   ##
==========================================
+ Coverage   60.49%   60.68%   +0.19%     
==========================================
  Files         170      172       +2     
  Lines       16836    16901      +65     
==========================================
+ Hits        10185    10257      +72     
+ Misses       5634     5624      -10     
- Partials     1017     1020       +3     
Flag Coverage Δ
unittests 60.68% <87.01%> (+0.19%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/context_commands.go`:
- Around line 12-15: showContextInfo currently forwards any recognized slash
command to sendPrompt when tokenUsage is empty, causing "/context" to be treated
like a user prompt; change showContextInfo so that if original is the "/context"
command (e.g., original == "/context" or strings.HasPrefix(original,
"/context")), it always emits the context status message via the same logic used
when tokenUsage.HasAny() is true (instead of calling sendPrompt), and only call
sendPrompt for non-slash inputs; reference showContextInfo, sendPrompt, and
tokenUsage.HasAny when applying this conditional handling.
🪄 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: 234e852e-5155-49d8-8bc2-86592554292b

📥 Commits

Reviewing files that changed from the base of the PR and between b3dd3a1 and f82aefc.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (22)
  • internal/assistant/anthropic_internal_test.go
  • internal/assistant/client.go
  • internal/assistant/context_build.go
  • internal/assistant/context_build_test.go
  • internal/assistant/context_usage.go
  • internal/assistant/lifecycle.go
  • internal/assistant/runtime.go
  • internal/assistant/runtime_events_test.go
  • internal/assistant/runtime_test.go
  • internal/assistant/usage.go
  • internal/assistant/usage_events.go
  • internal/assistant/usage_test.go
  • internal/model/usage.go
  • internal/terminal/app.go
  • internal/terminal/autocomplete.go
  • internal/terminal/commands.go
  • internal/terminal/context_commands.go
  • internal/terminal/render_parity_test.go
  • internal/terminal/render_test.go
  • internal/terminal/token_usage.go
  • internal/terminal/token_usage_export_test.go
  • internal/terminal/token_usage_test.go

Comment thread internal/terminal/context_commands.go
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
internal/model/usage_test.go (1)

11-49: ⚡ Quick win

Convert these helper tests to a table-driven structure.

These tests validate core TokenUsage behavior and should follow the repository’s preferred table-driven style for consistency and easier case expansion.

Suggested refactor sketch
 func TestTokenUsageHelpers(t *testing.T) {
-	t.Parallel()
-
-	usage := model.TokenUsage{
-		Breakdown:     nil,
-		ContextWindow: 100,
-		ContextTokens: 25,
-		InputTokens:   10,
-		OutputTokens:  15,
-	}
-
-	assert.Equal(t, 25, usage.TotalTokens())
-	assert.True(t, usage.HasAny())
-	assert.Equal(t, 25, usage.ContextPercent())
+	t.Parallel()
+	tests := []struct {
+		name           string
+		usage          model.TokenUsage
+		wantHasAny     bool
+		wantTotal      int
+		wantCtxPercent int
+	}{
+		{
+			name: "normal usage",
+			usage: model.TokenUsage{
+				Breakdown:     nil,
+				ContextWindow: 100,
+				ContextTokens: 25,
+				InputTokens:   10,
+				OutputTokens:  15,
+			},
+			wantHasAny:     true,
+			wantTotal:      25,
+			wantCtxPercent: 25,
+		},
+		{
+			name: "context percent capped",
+			usage: model.TokenUsage{
+				Breakdown:     nil,
+				ContextWindow: 100,
+				ContextTokens: 250,
+			},
+			wantHasAny:     true,
+			wantTotal:      0,
+			wantCtxPercent: 100,
+		},
+		{
+			name:           "empty usage",
+			usage:          model.EmptyTokenUsage(),
+			wantHasAny:     false,
+			wantTotal:      0,
+			wantCtxPercent: 0,
+		},
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+			assert.Equal(t, tc.wantHasAny, tc.usage.HasAny())
+			assert.Equal(t, tc.wantTotal, tc.usage.TotalTokens())
+			assert.Equal(t, tc.wantCtxPercent, tc.usage.ContextPercent())
+		})
+	}
 }

As per coding guidelines, "**/*_test.go: Prefer table-driven tests for core behavior and regression tests for terminal rendering bugs".

🤖 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/model/usage_test.go` around lines 11 - 49, Convert the three
separate tests into a single table-driven test: create a slice of test cases
(with name, TokenUsage fields: Breakdown, ContextWindow, ContextTokens,
InputTokens, OutputTokens, and expected values for TotalTokens, HasAny,
ContextPercent) and iterate with t.Run for each case, calling
usage.TotalTokens(), usage.HasAny(), and usage.ContextPercent() and asserting
expected values; include a case that uses model.EmptyTokenUsage() for the
"empty" scenario, and ensure each subtest calls t.Parallel() to keep parallel
behavior as in TestTokenUsageHelpers/TestEmptyTokenUsageHasNoUsage while keeping
references to the TokenUsage methods (TotalTokens, HasAny, ContextPercent) and
the EmptyTokenUsage helper.
internal/terminal/token_usage_test.go (1)

152-188: ⚡ Quick win

Prefer a table-driven test for the /context behavior variants.

These two tests share almost identical setup and can be consolidated into one table-driven test to make future regression cases cheaper to add.

♻️ Proposed refactor
-func TestShowContextInfoHandlesContextCommandWithoutUsage(t *testing.T) {
-	t.Parallel()
-	...
-}
-
-func TestShowContextInfoDisplaysSummaryAndBreakdown(t *testing.T) {
-	t.Parallel()
-	...
-}
+func TestShowContextInfo(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		name     string
+		original string
+		usage    model.TokenUsage
+		check    func(t *testing.T, msg string)
+	}{
+		{
+			name:     "context command without usage",
+			original: "/context",
+			usage:    model.EmptyTokenUsage(),
+			check: func(t *testing.T, msg string) {
+				assert.Equal(t, "context:", msg)
+			},
+		},
+		{
+			name:     "context command with summary and breakdown",
+			original: "/context now",
+			usage: model.TokenUsage{
+				Breakdown: map[string]int{
+					testBreakdownExtensions: 0,
+					testBreakdownHistory:    1200,
+					testBreakdownSystem:     50,
+				},
+				ContextWindow: 1000,
+				ContextTokens: 250,
+			},
+			check: func(t *testing.T, msg string) {
+				assert.Contains(t, msg, "context:")
+				assert.Contains(t, msg, "- ctx 250/1.0k 25%")
+				assert.Contains(t, msg, "- breakdown:\n  - history: 1.2k\n  - system: 50")
+			},
+		},
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+			app := terminal.NewAppForTest()
+			app.SetTokenUsageForTest(tc.usage)
+			require.NoError(t, app.ShowContextInfoForTest(tc.original))
+			messages := app.MessageContentsForTest()
+			require.NotEmpty(t, messages)
+			tc.check(t, messages[len(messages)-1])
+		})
+	}
+}

As per coding guidelines **/*_test.go: Prefer table-driven tests for core behavior and regression tests for terminal rendering bugs.

🤖 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/token_usage_test.go` around lines 152 - 188, Consolidate
the two tests TestShowContextInfoHandlesContextCommandWithoutUsage and
TestShowContextInfoDisplaysSummaryAndBreakdown into a single table-driven test:
define cases for "no usage" and "with usage" that each create an app via
terminal.NewAppForTest, optionally call app.SetTokenUsageForTest (only for the
"with usage" case) and then call app.ShowContextInfoForTest with the appropriate
command; for each case assert messages from app.MessageContentsForTest contain
the expected snippets (e.g. "context:" for both, and the summary/breakdown lines
only for the "with usage" case). Ensure each case runs t.Run and t.Parallel(),
and reuse the same setup/teardown logic from the original tests
(terminal.NewAppForTest, ShowContextInfoForTest, SetTokenUsageForTest) so future
variants can be added as table entries.
🤖 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/model/usage_test.go`:
- Around line 11-49: Convert the three separate tests into a single table-driven
test: create a slice of test cases (with name, TokenUsage fields: Breakdown,
ContextWindow, ContextTokens, InputTokens, OutputTokens, and expected values for
TotalTokens, HasAny, ContextPercent) and iterate with t.Run for each case,
calling usage.TotalTokens(), usage.HasAny(), and usage.ContextPercent() and
asserting expected values; include a case that uses model.EmptyTokenUsage() for
the "empty" scenario, and ensure each subtest calls t.Parallel() to keep
parallel behavior as in TestTokenUsageHelpers/TestEmptyTokenUsageHasNoUsage
while keeping references to the TokenUsage methods (TotalTokens, HasAny,
ContextPercent) and the EmptyTokenUsage helper.

In `@internal/terminal/token_usage_test.go`:
- Around line 152-188: Consolidate the two tests
TestShowContextInfoHandlesContextCommandWithoutUsage and
TestShowContextInfoDisplaysSummaryAndBreakdown into a single table-driven test:
define cases for "no usage" and "with usage" that each create an app via
terminal.NewAppForTest, optionally call app.SetTokenUsageForTest (only for the
"with usage" case) and then call app.ShowContextInfoForTest with the appropriate
command; for each case assert messages from app.MessageContentsForTest contain
the expected snippets (e.g. "context:" for both, and the summary/breakdown lines
only for the "with usage" case). Ensure each case runs t.Run and t.Parallel(),
and reuse the same setup/teardown logic from the original tests
(terminal.NewAppForTest, ShowContextInfoForTest, SetTokenUsageForTest) so future
variants can be added as table entries.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 67fcde32-79a7-4529-bedf-8ba6a10e7659

📥 Commits

Reviewing files that changed from the base of the PR and between f82aefc and 11a1208.

📒 Files selected for processing (4)
  • internal/model/usage_test.go
  • internal/terminal/context_commands.go
  • internal/terminal/token_usage_export_test.go
  • internal/terminal/token_usage_test.go

@omarluq omarluq merged commit f64fe71 into main May 23, 2026
13 checks passed
@omarluq omarluq deleted the feat/context-seam-debug branch May 23, 2026 23:17
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.

2 participants