Skip to content

feat(coordinator): replace turn budget with per-cycle USD cost budget (REHOR-211) - #588

Open
Hyperkid123 wants to merge 1 commit into
OpenShift-Fleet:masterfrom
Hyperkid123:feat/rehor-211-cost-budget
Open

Hyperkid123 wants to merge 1 commit into
OpenShift-Fleet:masterfrom
Hyperkid123:feat/rehor-211-cost-budget

Conversation

@Hyperkid123

Copy link
Copy Markdown
Contributor

Description

Replaces the turn/tool-call budget with a per-cycle-run USD cost budget (default $5, configurable), fixing the semantic mismatch where maxTurns: 200 meant 200 model turns on the Claude runtime but 200 tool calls (root + subagents) on OpenCode — ~10x stricter, which killed delegation-heavy runs mid-wrap-up (evidence: RHCLOUD-52555 cycle 52325, terminal failed/max_turns at 23 root turns with 568 tool calls, PR already open, state not saved).

REHOR-211

What changed:

  • CostBudgetPolicy replaces TurnBudgetPolicy (both in coordinator/src/coordinator.ts): a shared UsageLedger (domain/usage.ts, snapshot rules identical to the compatibility projection) accumulates cost from usage events across root and subagent sessions. Thresholds 50% info / 75% warning / 90% critical / 100% exhausted — each fires once with an agent-facing notice telling the agent to save state via task_update. Grace window: at 100% the agent is told to wrap up; hard abort at 115% with terminal reason budget_exceeded (expected-failure handling, same as the old max_turns).
  • maxTurns removed from the coordinator contract: rehor-run.v1.json limits are now timeoutMs + budgetUsd; default $5 flows through the TS python bridge (DEFAULT_BUDGET_USD), bot/config.py (budget_usd, default 5.0), and coordinator_bridge.py. The Claude SDK maxTurns pass-through is removed; enforcesTurnLimit is now false on both runtimes.
  • Budget notices now reach OpenCode agents — previously turn-budget warnings were transcript-only (known gap): new onPolicyNotice push channel on AgentRuntimeRunContext; the OpenCode runtime delivers via a queued root-session prompt (fire-and-forget, failures logged and non-fatal), the Claude runtime flushes a notice queue through the PostToolUse hook systemMessage.
  • Unknown pricing fails open: one-time COST BUDGET INACTIVE policy event when cost is affirmatively source: "unknown" (unsettled in-flight snapshots do not disable the budget); the cycle timeout remains the backstop.
  • Metric devbot_cost_budget_event_total (info/warning/critical/exhausted); docs updated (coordinator README, opencode-migration, openshell-deployment, presets-design, root README example config).

Deliberately out of scope (documented in the ticket): the legacy bot/agent.py Python turn hook stays until the legacy direct path retires; opencode.maxToolCalls / REHOR_OPENCODE_MAX_TOOL_CALLS are now parsed-but-unused removal candidates.


Blast radius

  • Coordinator (TS), python bridge contract, bot config. TS coordinator + python bridge ship in one image, so the budgetUsd contract change is atomic; instance configs without budgetUsd get the $5 default.
  • No memory-server, dashboard, or deploy-template changes required (cost columns/metrics already exist; new metric name is additive).
  • Tested against: coordinator unit/integration/contract suites (new cost-budget-policy.test.ts, 10 tests; 4 new integration tests incl. 115% abort and unknown-pricing fail-open; reworked OpenCode runtime tests for hard-limit abort, grace window, notice delivery), python bot/tests (config + coordinator_bridge updated for budgetUsd).

Rollback plan

git revert of the single commit is sufficient — the schema/budget field is only consumed by code shipped in the same image, stored transcripts/events are unaffected (policy payload is a new shape rendered generically), and instance configs without budgetUsd fall back to the default either way. After revert, turn budget is restored with the old maxTurns semantics.


Checklist

  • Tested against at least one consuming repo/service
  • No breaking changes to existing consumers (or migration path documented)
  • No hardcoded secrets, tokens, or passwords
  • Container images pinned to specific tags, not latest

AI disclosure

Assisted by: OpenCode (GLM 5.3) — implementation delegated to and reviewed by the primary session; all tests/typecheck/lint run locally and independently re-verified.

@Hyperkid123
Hyperkid123 force-pushed the feat/rehor-211-cost-budget branch from 65c43c6 to 66bc807 Compare October 27, 2026 13:59

This branch has not been deployed

No deployments
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.

1 participant