Repository navigation
Agent spend is complete: agents.open and agent.changed carry the saved figure, and late receipts save it - #2347
Merged
Merged
Conversation
…ay so A receipt recovered after a cancelled turn ended moved the meter and the machine ledger but never meta.json, so every reader of the saved total under-reported until another turn sealed. reconciled now stamps the cumulative meter after banking. Spend stamps hold one mutex from capture to write, so an older captured total cannot land after a newer one, and each successful save is told to OnSpendSaved with the conversation's folder and the saved figure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….list reports Only agents.list set Agent.spent_usd. The open answer and every agent.changed now carry the figure saved in meta.json, read in the same step that says the agent (beside unread), so a view copied earlier cannot deliver an older figure. A spend save restates the agent once, off the stamping goroutine, when the figure differs from what was last said. Other method answers keep their path and omit the figure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eceipts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts.open Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guard law requires every critical section in the guarded tree to unlock from a defer directly under its Lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
CI on the final head
🤖 Generated with Claude Code |
This was referenced Nov 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two generic gaps in an agent's saved spend (
Agent.spent_usd, the cumulative figure in the conversation'smeta.json):agents.openandagent.changednever carried it. Onlyagents.list(listedView) setSpentUSD; the cachedview behind open answers and every
agent.changedhad none, so a client that takes those over its list row lostthe figure.
(*Agent).reconciled(Cancelled calls keep the money that was measured #2302) banked the latereceipt into the meter and the machine ledger, but nothing stamped
meta.jsonand no per-agent change was sent, soagents.listunder-reported until something else rewrote the file.Change
internal/session:reconciledstamps the cumulative meter after banking (the same snapshot door a turn's sealuses; no ledger or call-line sums on top). Spend stamps hold one mutex from capture to write, so an older captured
total cannot be written after a newer one. Every successful save is told to a new process-wide hook,
OnSpendSaved(SpendSaved{Dir, SpentUSD}), in the style ofOnUsageWritten.cmd/codeaf: saved spend is read frommeta.jsonin the same step that says an agent (beside unread, underunreadSay), for everyagent.changedthrough the inbox tap and for the open answer; there is no app-side spendcache, so a view copied earlier can never deliver an older figure. A save restates the agent once, off the stamping
goroutine (coalesced per conversation), only when the figure differs from what was last said. Other method answers
keep their existing path and omit the figure.
agents.budget.getkeeps the live meter.Known and unchanged: after a resume the restored meter sums journal usage lines and not late call lines, so a later
turn's stamp can save a figure lower than a late charge saved earlier (pre-existing replay gap; not repaired here).
Validation contract
agents.openreports the same spend asagents.listfor the same agentcmd/codeaf/appspendsaved_test.go:TestAgentsOpenAndListAgreeOnSavedSpend(cold and held, both folder layouts), plus the stale-snapshot and stale-row regressionsagents.listreports the higher spend and oneagent.changedcarries itinternal/session/spendsaved_test.go:TestLateRecoveredCancellationSavesCumulativeSpendOnce,TestAnsweredTurnPublishesItsSavedSpend;cmd/codeaf/appspendsaved_test.go: news sends exactly one change, duplicates none; a stalled emitter does not hold the stampTestLateRecoveredCancellationSavesCumulativeSpendOnce(X+L saved, one ledger row for L);TestConcurrentTurnAndLateSpendStampsKeepTheLatestMeter(an older capture cannot overwrite a newer total)Reviewed by fresh independent review passes; findings from earlier rounds (stale views, a blocking hook, an untested
capture race, method answers waiting on a stalled event writer) are fixed and covered above.
🤖 Generated with Claude Code