Skip to content

fix(usage): dedup provider-wide notes and add report-level notes field#3312

Open
oldschoola wants to merge 4 commits into
can1357:mainfrom
oldschoola:fix/usage-provider-notes-dedup-3268-clean
Open

fix(usage): dedup provider-wide notes and add report-level notes field#3312
oldschoola wants to merge 4 commits into
can1357:mainfrom
oldschoola:fix/usage-provider-notes-dedup-3268-clean

Conversation

@oldschoola

Copy link
Copy Markdown
Contributor

Problem

Provider-wide disclaimers (e.g. OpenCode Go's "OMP-observed spend only") were duplicated onto every UsageLimit.notes, then repeated N times in the TUI aggregate renderer — once per account × window. With 2 accounts × 3 windows, the same disclaimer appeared 6 times bullet-joined.

Fixes #3268

Changes

Structural fix

  • Added notes?: string[] to the UsageReport interface (the type source of truth)
  • Added the field to both schema copies:
    • packages/ai/src/usage.ts usageReportSchema
    • packages/ai/src/auth-broker/wire-schemas.ts arkUsageReportSchema — critical because the broker client validates /v1/usage responses with usageResponseSchema which uses "+": "reject", so an undeclared field would be rejected on deserialization
  • Moved opencode-go's disclaimer from per-limit notes to provider-level notes

Defensive fix

  • Deduplicated identical per-limit notes in the TUI aggregate renderer (command-controller.ts) via [...new Set(...)]
  • Rendered provider-level notes once above per-account sections in all three rendering paths: TUI (command-controller.ts), CLI (usage-cli.ts), ACP (usage-report.ts)

Tests

  • usage-report-notes-schema.test.ts (new): wire-schema round-trip proving notes survives usageResponseSchema validation
  • usage-cli.test.ts: provider-level notes render once, not duplicated per account or limit
  • All 51 affected tests pass across both packages
  • bun check passes (all 16 packages)

Provider-wide disclaimers (e.g. OpenCode Go's "OMP-observed spend
only") were duplicated onto every UsageLimit, then repeated N times
in the TUI aggregate renderer (once per account × window). With
2 accounts × 3 windows, the same disclaimer appeared 6 times
bullet-joined.

Structural fix:
- Add notes?: string[] to UsageReport (interface + both schema
  copies: usage.ts and auth-broker/wire-schemas.ts) so the field
  survives the broker client's "+": "reject" deserialization gate.
- Move opencode-go's disclaimer from per-limit notes to
  provider-level notes.

Defensive fix:
- Dedup identical per-limit notes in the TUI aggregate renderer
  (command-controller.ts) via [...new Set(...)].
- Render provider-level notes once above per-account sections in
  all three rendering paths: TUI (command-controller), CLI
  (usage-cli), and ACP (usage-report helper).

Regression tests:
- usage-cli.test.ts: provider-level notes render once, not
  duplicated per account or limit; positioned above per-account rows.
- usage-report-notes-schema.test.ts: wire-schema round-trip proving
  notes survives usageResponseSchema validation.

Fixes can1357#3268
@github-actions github-actions Bot added the vouched Passed the vouch gate label Jun 23, 2026
@roboomp roboomp added cli CLI commands and arguments fix provider:opencode-go providers LLM provider-specific issues review:p1 triaged tui Terminal UI rendering and display labels Jun 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5225f54f6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// above the per-account sections instead of duplicating onto every limit.
const providerNotes = [...new Set(providerReports.flatMap(report => report.notes ?? []))];
if (providerNotes.length > 0) {
lines.push(` ${uiTheme.fg("dim", providerNotes.join(" • "))}`.trimEnd());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Sanitize report-level notes before rendering

When a provider returns a report-level note with tabs, embedded newlines, or an overlong string, this new TUI path injects it directly into the transcript. The root AGENTS.md TUI Sanitization section requires raw provider/tool output to be normalized (e.g. replace tabs and truncate) before display; otherwise /usage can create terminal holes, wrapping, or overflow. Please sanitize the joined note text before passing it to uiTheme.fg.

Useful? React with 👍 / 👎.

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

P1: tight root fix for provider-wide usage notes; schema and changelog updates are coherent.
Inline should-fix: the TUI aggregate per-limit note dedup is untested; the added CLI test exercises the opposite contract.
Verification: bun test packages/ai/test/usage-report-notes-schema.test.ts passed. bun test packages/coding-agent/test/usage-cli.test.ts could not start because packages/coding-agent/src/export/html/index.ts imports missing ./tool-views.generated.js in this worktree.
Thanks for the focused fix.

lines.push(` ${uiTheme.fg("dim", resetText)}`.trimEnd());
}
const notes = sortedLimits.flatMap(limit => limit.notes ?? []);
const notes = [...new Set(sortedLimits.flatMap(limit => limit.notes ?? []))];

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.

should-fix: This is the only changed path that deduplicates per-limit notes across the TUI aggregate groups, but the added test coverage is in formatUsageBreakdown() and explicitly expects two occurrences for the same note. A regression here would still pass. Please add coverage through /usage/handleUsageCommand (or another exported TUI path) asserting one Overage requests: 5 when two reports share the same provider/window group.

Address review feedback: provider notes could contain tabs, embedded
newlines, or control characters that break TUI rendering. Both note
rendering sites (provider-wide and per-group) now wrap the joined text
through sanitizeText → truncateToWidth → replaceTabs per AGENTS.md
TUI Sanitization rules.
@oldschoola

Copy link
Copy Markdown
Contributor Author

Fixed in the latest push. Both note rendering sites (provider-wide at line 1592 and per-group at line 1666) now sanitize the joined text through sanitizeText() → truncateToWidth() → replaceTabs() per AGENTS.md TUI Sanitization rules, preventing tabs, embedded newlines, and control characters from breaking terminal rendering.

Apply sanitizeText to provider notes in the shared usage-report
renderer and the CLI usage output, matching the sanitization added
to command-controller.ts. Prevents tabs/newlines/control characters
in provider notes from breaking terminal rendering.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58de02ea05

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const providerNotes = [...new Set(providerReports.flatMap(report => report.notes ?? []))];
if (providerNotes.length > 0) {
lines.push(
` ${uiTheme.fg("dim", replaceTabs(truncateToWidth(sanitizeText(providerNotes.join(" • ")), 110)))}`.trimEnd(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize note newlines before TUI rendering

When a provider or broker returns a report-level note containing \n, this still injects that newline into the rendered transcript row because sanitizeText(...) preserves LF; the continuation line is not prefixed or independently width-limited, so a note like "caveat\nvery long text..." can break the /usage TUI layout despite this sanitization path. Fresh evidence in the current patch is that the new line still passes the joined provider notes through sanitizeText directly instead of splitting or replacing newlines before truncation.

Useful? React with 👍 / 👎.

sanitizeText preserves newlines (\n) which break TUI line layout when
injected into a single rendered row. All notes rendering sites now
replace \r\n sequences with spaces before sanitization:

- command-controller.ts: provider-wide notes (line 1591) + per-group
  notes (line 1666)
- usage-report.ts: provider-wide notes (line 58) + per-limit notes
  (line 90, previously completely unsanitized)
- usage-cli.ts: provider-wide notes (line 455)
@oldschoola

Copy link
Copy Markdown
Contributor Author

Fixed in the latest push. All notes rendering sites now normalize newlines (replace(/[\r\n]+/g, " ")) before sanitizeText, preventing embedded newlines from breaking TUI line layout. This covers 5 sites across 3 files:

  • command-controller.ts: provider-wide notes + per-group notes
  • usage-report.ts: provider-wide notes + per-limit notes (previously completely unsanitized)
  • usage-cli.ts: provider-wide notes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI commands and arguments fix provider:opencode-go providers LLM provider-specific issues review:p1 triaged tui Terminal UI rendering and display vouched Passed the vouch gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

omp usage: duplicate notes and cramped multi-account layout in command-controller.ts

2 participants