Repository navigation
Spend survives a restart: late receipts and abandoned turns are restored; turn.tool carries live spend - #2612
Merged
Merged
Conversation
Carries #2413 from the closed integration PR #2306 (workstreams/main); it never reached dev. Co-Authored-By: Claude Mythos 5.2 <[email protected]>
…less than it charged Carries #2414 from the closed integration PR #2306 (workstreams/main); it never reached dev. The one conflict, Digest's fields, keeps dev's folded-usage total beside the late-receipt sum. Co-Authored-By: Claude Mythos 5.2 <[email protected]>
Carries #2440 from the closed integration PR #2306 (workstreams/main); it never reached dev. Co-Authored-By: Claude Mythos 5.2 <[email protected]>
Co-Authored-By: Claude Mythos 5.2 <[email protected]>
AbirAbbas
marked this pull request as ready for review
November 12, 2026 20:10
The transcript-format section of sessions-and-rewind.md still said every `call` line is never added up. Since this pull request a resume adds a call line marked with an `arm` once, as the usage and call-line sections of the same page already say. The section now names that exception, and a test keeps every paragraph that says call lines are not added up naming the arm beside it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…2612) The abandoned tool-batch, settle-bound and final stage-report exits wrote their step seal to the journal but never stamped meta.json. Their steps bank as turn calls, which Agent.bank leaves for the seal to stamp, so meta.json kept the figure from before them. After a restart the meter (/cost, the status row, agents.budget.get) restored the journal's total while meta.json (agents.list spent_usd, Home) kept the older one, because stampRestoredSpend fills only a missing figure. A .01 title followed by a .25 step at any of the three exits read $0.26 on the meter and $0.01 in meta.json until the next ordinary turn sealed. The three exits now share sealSteps, which writes the step seal without duration and owes the deferred spend stamp. The stamp reads the live books and writes only the spend fields, so it changes nothing a replacement turn owns. The manual and the change entry say so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
AbirAbbas
added a commit
that referenced
this pull request
Nov 13, 2026
Brings in #2613, #2612, #2614, #2618 and #2615. #2618 changes the visibility check this branch's job-events cursor reads: a spent GitHub rate limit returns GitHub's retry time instead of a cached denial, and is remembered per token until then. The two meet in repoAllowed, which this branch made answer (allow, known). A remembered limit answers false, false: a limit says nothing about the repository, so the job-events page ends before it as before any undecided one, as a fresh limit already does (askRepo's cache=false). Co-Authored-By: Claude Opus 5.5 <[email protected]>
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
Three spend changes that were reviewed and merged into
workstreams/mainbut never reacheddev. Their integration PR, #2306, is closed: its baseapp/v3was retired by #2561, and #2597 left it out.devstill has both faults and lacks the wire field (evidence below), so they are carried here, one commit each.callline marked with anarm. Replay summed usage seals only, so a restart forgot that money: .25 sealed + .125 late saved .375, a resume read .25, and the next .01 turn saved .26. Replay andDigest.Spend()now count a call line that carries an arm as one call and no turn. Ordinary call lines stay evidence. A paid arm line is also admitted afterClose, as usage seals already were.abandonedline, which replay never bills. The displaced loop now writes its own step tally as a usage seal without duration when it leaves its tool batch. A settle turn stopping at its bound and a final stage report write the same seal. Theabandonedline stays evidence.turn.toolcarries the conversation's live spend (turn.tool carries the conversation's live spend so far #2413). Optionalspent_usd, the same meter asagents.budget.get'sengine.spent_usd, read after the emitter has released its locks. Zero or unknown readings and history rows omit it. No new event, method or capability. The desktop app already reads this field.What changed against the originals
internal/session/digest.go.devaddedDigest.folded(folded usage); the late-receipt sum sits beside it andSpend()adds both.Evidence that
devstill has the faultsWith only the original test files placed on
dev(80b5861), no code change:Four of the 7 abandoned-turn tests failed (the other three assert behaviour that holds either way), and all 4 late-receipt tests failed. The live-spend tests did not compile:
unknown field SpentUSD in struct literal of type TurnTool.Tests
CI on 3800cf4: every check passes, after one rerun of
touched codeaf. Please look at why it needed one:TestFinalPublishDisplacementReachesTheRestoredWindowOnce/engine/displaced=truefailed withfinal publish did not encounter displacement, in its shard and again on the classifier's solo retry. The base probe skipped the test (no engine binary in the base worktree), so attribution was inconclusive. The rerun of the job passed. Locally the subtest passed 25 of 25. It exercises sync's final publish at quit, which this PR does not touch, and it is not yet listed in touched packages: flaky or already-failing tests #1749. A later repeat on macOS, with the engine built in both trees so nothing skips, passed 300 of 300 on this branch (3800cf4) and 300 of 300 on unmodifieddev(80b5861), at 1, 2 and 4 CPUs. So the CI failure has not been reproduced on either tree and is still not attributed.TestProcessCloseJoinsTheUsageWriterandTestDoReturnsAfterCancelledAccountingAndItsWriterStopwere classified flaky, as on many unrelated runs in touched packages: flaky or already-failing tests #1749.Local, macOS, focused runs with the fix: pass.
go test -run 'Abandon|SettleBound|StageBatch|StageReport|LateRe|LateCall|Reconcil|Digest|RestoredUsage|CallLines|SpendStamp|Seal' ./internal/session/: ok, 98 top-level tests../internal/cellindex/and./internal/appwire/: ok.Local
make pr-readydid not pass on this Mac. The PR is marked ready on the strength of CI and the comparison withdevbelow. The light gate andtouched restpassed. The heavy legs are red here for reasons that are also red on unmodifieddev(80b5861) on the same machine:touched session: 5 failures (TestACommandLookFailsOnAFailingPipeStage,TestWatchTickFailsOnAFailingPipeStage,TestBashBeltTruncationCapFollowsTheWindow,TestBashBeltTruncationWritesHeadTailAndPath,TestTheFixedPrefixStaysUnderItsBudget). All 5 fail ondevhere too.touched codeaf: 14 failures while other suites were running on the box. 9 are Factory and memory-worker tests that fail ondevhere too, alone. The other 5 pass when run alone on this branch.TestATrackedConversationIsReachedAndAForgottenOneIsNot(TempDir RemoveAll cleanup: ... directory not empty); it passed 3 of 3 alone and on the second attempt.Changes after review
Two review findings, both fixed in commits on top of 3800cf4. The CI run described under Tests was on 3800cf4, before these commits.
The manual's transcript-format section contradicted the code (6354eb8).
internal/manual/chat/sessions-and-rewind.md, the section "What is saved in a conversation transcript" still said acallline is never added up. Since this PR a resume does add acallline marked with anarm(a late receipt), once, as the page's own usage and call-line sections already say.callline is normally not added up, because its money is already in theusageline that closed its turn, and that a late receipt marked with anarmis the exception: its money is in nousageline, so a resume adds it once.TestTheManualNamesTheArmWhereverItSaysCallLinesAreNotAddedUp(internal/session/spend_replay_test.go) reads the page and requires every paragraph that says call lines are not added up to name thearmexception. It failed on the old paragraph and passes now. The behaviour itself was already covered byTestLateCallLinesRestoreSpendWithoutAddingTurnsandTestDigestSpendIncludesLateReceiptsWithoutAddingUsageLines.The three new step seals did not save the conversation's total to
meta.json(eca765e).meta.json. Their steps bank as turn calls, whichAgent.bankleaves for the seal to stamp, and these exits do not go throughsealTurn. Someta.jsonkept the figure from before the steps. After a restart with no further turn, the meter (/cost, the status row,agents.budget.get) restored the journal's total, whilespent_usdinagents.list/agents.open/agent.changedand the Home card kept the older figure, becausestampRestoredSpendonly fills in a missing figure. Example: a $0.01 title, then a $0.25 step ending at one of these exits, read $0.26 on the meter and $0.01 inmeta.json. The same gap existed before any restart, until the next ordinary seal; a Factory stage chat that ends on its stage report may never seal again.Agent.sealSteps, which writes the step seal without duration as before and then calls the existing deferredstampSpend. That stamp writes onlySpentUSD/Tokensfrom the live books and does not touchLastReplyAtor anything a replacement turn owns. The manual's usage-lines paragraph and the change entry (docs/changes/unreleased/2612-abandoned-turn-step-seal.md, whose surface now includesapp) say so.TestAStepSealSavesTheTotalAResumeRestores(internal/session/abandon_spend_test.go), subtestsabandoned,settle_boundandfinal_report. Each checksmeta.jsonagainst the live meter before a restart, then compares the resumed meter,SavedSpend(whatagents.listreads) and the journal'sDigest.Spend(). All three failed before ($0.26 on the meter, $0.01 inmeta.json) and pass now, also with-count=8and with-race -count=3alongside the other abandoned-turn tests.Evaluated and left: for the second finding the reviewer also suggested letting
stampRestoredSpendraisemeta.jsonon resume. Not done: it would fix only the after-restart symptom and leave the gap between the step seal and the next ordinary seal while the engine is still running.🤖 Generated with Claude Code