mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
fix: serialize goal progress accounting (#26155)
## Why Goal progress accounting can be reached from multiple completion paths for the same thread. Each path takes a progress snapshot, writes the usage delta, and then marks that snapshot as accounted. When two tool-completion hooks run at the same time, they can both observe the same unaccounted delta and charge it twice. ## What changed - Added a per-thread progress-accounting permit to `GoalAccountingState`. - Held that permit across the snapshot/write/mark-accounted critical section for active-turn, idle, and tool-finish accounting. - Added regression coverage for parallel tool-finish hooks so a shared token delta is charged once and only one progress event is emitted. ## Testing - Not run locally. - Added `parallel_tool_finish_accounts_active_goal_progress_once`.
This commit is contained in:
5 files changed
+105
-1
No files matched your search
@@ -291,6 +291,15 @@ impl GoalToolExecutor {
|
||||
let Some(turn_id) = self.accounting_state.current_turn_id() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let _accounting_permit = self
|
||||
.accounting_state
|
||||
.progress_accounting_permit()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
FunctionCallError::Fatal(format!(
|
||||
"goal progress accounting semaphore closed: {err}"
|
||||
))
|
||||
})?;
|
||||
let Some(snapshot) = self.accounting_state.progress_snapshot(turn_id.as_str()) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user