Skip to content

fix: attribute compaction summary costs to the model that generated them#3760

Merged
dgageot merged 3 commits into
docker:mainfrom
dgageot:fix/compaction-cost-per-model
Jul 20, 2026
Merged

fix: attribute compaction summary costs to the model that generated them#3760
dgageot merged 3 commits into
docker:mainfrom
dgageot:fix/compaction-cost-per-model

Conversation

@dgageot

@dgageot dgageot commented Jul 20, 2026

Copy link
Copy Markdown
Member

When an agent uses a compaction_model, the /cost dialog's "By Model" section showed nothing for that model — its token spend was silently folded into the session total and a generic "compaction" bucket, with no per-model breakdown. This made it impossible to reason about or compare compaction costs across models.

The fix threads the compaction model name and token usage from the compaction sub-run through compactor.Result, into session.Item (new model and usage_json columns via SQLite migration 25), through SessionSummaryEvent, and into the TUI cost dialog, which now attributes compaction spend to the model that generated the summary and includes its tokens in the per-model totals.

Review follow-ups are bundled in a second commit: a shared chat.Usage.Add helper, server-side validation of summary attribution metadata (negative token counts, model name length and control characters, zero-usage normalization), Go client API carrying the full summary item with "tokens" wire-name compatibility for first_kept_entry, deep-copied usage pointers, and a standalone fix preserving FirstKeptEntry when branching summary items. New tests cover compactor model/usage reporting, SQLite round-trip, persistence observer, server validation, branch cloning, and the dialog's by-model attribution.

dgageot added 3 commits July 20, 2026 13:32
Adds Model+Usage to compactor.Result and session.Item (migration 25),
threads them through SessionSummaryEvent, persistence, and the server
AddSummary path so the TUI /cost By Model breakdown includes compaction
spend instead of silently dropping it.
Add chat.Usage.Add helper; validate/normalize summary attribution in
server; thread model/usage through client API via session.Item; deep-copy
usage pointer in persistence observer and snapshotItems; document
last-model-wins in runUsage.

Assisted-By: Claude
@dgageot
dgageot requested a review from a team as a code owner July 20, 2026 14:54

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

Assessment: 🟢 APPROVE

The PR threads compaction model/usage attribution through a well-structured chain: compactor.Resultsession.ItemSessionSummaryEventPersistenceObserver → SQLite store (migration 25). The implementation is sound:

  • Deep-copy discipline is maintained throughout — usage pointers are copied in Clone, snapshotItems, cloneSessionItem, and the persistence observer, preventing aliasing bugs.
  • Migration 25 correctly adds NOT NULL DEFAULT '' columns to session_items without breaking existing rows.
  • Wire protocol preserves backward compat via the legacy tokens JSON field in AddSummaryRequest.
  • Server-side validation (validateSummaryAttribution) correctly guards against negative token counts, overlong model names, and control characters before persisting.
  • Cost dialog (addCompactionCost) correctly attributes compaction spend to the model bucket and accumulates tokens into per-model totals.
  • Branch/clone: cloneSessionItem correctly copies the new Model field and deep-copies the Usage pointer; FirstKeptEntry preservation in branch summary items is handled.

No high or medium severity bugs were found. Low-severity observations (not blocking):

  • runUsage in compactor.go reads sess.Messages without acquiring the session mutex — safe in the current single-owner context but inconsistent with the codebase's snapshotItems() convention.
  • addCompactionCost substitutes a zero chat.Usage{} for nil — harmless, but leaves a zero-token row in the By Agent compaction bucket for pre-migration summary items.

@dgageot
dgageot merged commit b4faf8f into docker:main Jul 20, 2026
10 of 11 checks passed
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.

3 participants