Repository navigation
fix(budget): enforce token grants and record provider cost in the ledger - #199
Merged
Merged
Conversation
INFORMATIONAL_USAGE_UNITS (cost_usd, neurons) are tracked for reporting and must never gate execution. They belong to ALLOWED_BUDGET_UNITS so that _validate_budget_amounts accepts them as recorded usage, but that also makes them grantable -- and get_exhausted_units iterated granted_budget without excluding them. A grant carrying cost_usd would therefore raise ResourceBudgetError and halt the run on a unit documented as advisory. Skip informational units in the exhaustion probe so enforcement matches the documented contract. Enforced units (tokens, wall_clock_seconds, gpu_hours) are unaffected; the added test asserts a maxed-out cost_usd yields no exhaustion while tokens still does.
This was referenced Sep 15, 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
Budget enforcement was advisory: a run could overrun its token grant with nothing
throttling it, and no neuron or USD figure ever reached a run artifact. This makes
grant admission actually gate execution, adds
BudgetLedger.get_exhausted_units()as the exhaustion probe, accumulates provider cost metering in the ledger, and
escalates a
ResourceBudgetErrorto severityerroreven whenrequire_budget_grantis False.Informational units (
cost_usd,neurons) are tracked for reporting and nevergate execution.
Fixes #81
Fixes #85
Scope
praxist/core/{budget,execution_guards, ledgers,modeling,protocol,replay,runtimes}.py,codex_sdk/_events.py,research_loop/stage.py. Adds one public method(
get_exhausted_units) and one constant (INFORMATIONAL_USAGE_UNITS).will now be halted at the grant boundary, which is the intended correction rather
than a regression. Existing ledgers are read unchanged; no migration.
shared guard layer, independent of any task's metric or protocol.
Verification
Capability validation of the budget gate, ledger and cost accounting, using the
branch's own
_write_budget_granthelper so grants are constructed exactly as itstests construct them:
tokens:100, usedtokens:100exhausted_units == ['tokens']— guard firestokens:100, usedtokens:50, cost_usd:99exhausted_units == []— informational tracked, not enforcedtokens:100, cost_usd:1.0, usedcost_usd:1.0['cost_usd']— inconsistent, fixed in this branchProbe C found that two modules disagreed about whether informational units are
enforceable:
append_usageskipped them when absent fromgranted_budget, butthis branch also added them to
ALLOWED_BUDGET_UNITS, so a grant could legallycontain
cost_usdwhileget_exhausted_unitsiteratedgranted_budgetwith noinformational exclusion — turning a reporting-only unit into a hard run halt. The
exclusion is now explicit in
get_exhausted_units, with a regression test.Full checks, run on this branch merged with current
main(a clean merge — noconflicts;
main's recent commits touch none of the files above):ruff check praxist/ tests/— All checks passedruff format --check praxist/ tests/— 450 files already formattedpyrefly check— 0 errorspytest tests/unit --tb=short -q— 2913 passedrun_test_coverage.py unit --fail-under 90 --fail-under-statements 95— passed,93.26% total / 95.01% statements
run_test_coverage.py integration— passedpytest(full) — 3353 passed, 7 skipped, 285 subtestsChecklist
.github/CONTRIBUTING.mdand the contribution terms inLICENSE.md.