Summary
grapharc run accepts only --max-tokens as a ceiling. Budget supports four dimensions — max_tokens, max_iterations, max_seconds, max_concurrency — and the other three are unreachable from this command. The code says so plainly:
# `Budget()` is genuinely unlimited on every dimension, so with no
# `--max-tokens` the budget check passes anything. An earlier comment here
# claimed the opposite; an adversarial run admitted a 200-node chain whose
# worst case was 400,000 tokens. The meter is real only when a ceiling is.
meter = BudgetMeter(Budget(max_tokens=max_tokens) if max_tokens else Budget())
Why this matters
Bounded work is one of the load-bearing promises here: max_seconds is delivered as an interrupt into a running node, not polled between them, and iterations are metered by the runtime itself. Those mechanisms exist and are tested — they are simply not wired to this command's flags. Making every ceiling reachable from the surface an operator actually types is what turns graph engineering from a design claim into something you can hold a run to.
Where in the code
grapharc/cli/graphrun.py — around line 157, where the meter is built
grapharc/cli/main.py — the run subparser, where --max-tokens is declared (add the siblings next to it)
grapharc/runtime/budget.py — Budget, for the exact field names and their semantics
grapharc/cli/plan.py — for how another command threads limits from flags into a budget
What to change
- Add
--max-seconds, --max-iterations and --max-concurrency to the run subparser, matching the existing --max-tokens style (type, metavar, help text).
- Thread them into the
Budget(...) construction, passing only what was supplied so an unset flag stays unlimited.
- Include the resolved ceilings in the
--json payload alongside max_tokens, so a run's limits are recoverable from the document.
- Add CLI tests in the style of
tests/test_cli.py asserting each flag reaches the budget.
- Update the README bullet that currently says
--max-tokens is the only ceiling.
Check whether these belong in the grapharc.toml config layer too (grapharc/cli/config.py has a KEYS table) — if you add them there, the resolution order and the sources provenance block must keep working.
How to verify
./.venv/bin/grapharc run <graph.json> --max-seconds 5 --json | grep max_seconds
uv run pytest tests/test_cli.py -q
uv run pytest -q
uv run ruff check .
Acceptance criteria
Skill level — good first issue
Mostly argparse plumbing with a clear existing example one line away, and the CLI test file shows exactly how to assert it. A good introduction to how flags, config resolution and the --json contract fit together in this repo. Comment here if you would like a pointer on the config layer part, which is optional.
Summary
grapharc runaccepts only--max-tokensas a ceiling.Budgetsupports four dimensions —max_tokens,max_iterations,max_seconds,max_concurrency— and the other three are unreachable from this command. The code says so plainly:Why this matters
Bounded work is one of the load-bearing promises here:
max_secondsis delivered as an interrupt into a running node, not polled between them, and iterations are metered by the runtime itself. Those mechanisms exist and are tested — they are simply not wired to this command's flags. Making every ceiling reachable from the surface an operator actually types is what turns graph engineering from a design claim into something you can hold a run to.Where in the code
grapharc/cli/graphrun.py— around line 157, where the meter is builtgrapharc/cli/main.py— therunsubparser, where--max-tokensis declared (add the siblings next to it)grapharc/runtime/budget.py—Budget, for the exact field names and their semanticsgrapharc/cli/plan.py— for how another command threads limits from flags into a budgetWhat to change
--max-seconds,--max-iterationsand--max-concurrencyto therunsubparser, matching the existing--max-tokensstyle (type, metavar, help text).Budget(...)construction, passing only what was supplied so an unset flag stays unlimited.--jsonpayload alongsidemax_tokens, so a run's limits are recoverable from the document.tests/test_cli.pyasserting each flag reaches the budget.--max-tokensis the only ceiling.Check whether these belong in the
grapharc.tomlconfig layer too (grapharc/cli/config.pyhas aKEYStable) — if you add them there, the resolution order and thesourcesprovenance block must keep working.How to verify
Acceptance criteria
grapharc run--jsonreports the ceilings actually in forceBudget--max-tokensis the only ceiling" bullet is updateduv run pytestgreen,uv run ruff check .cleanSkill level — good first issue
Mostly argparse plumbing with a clear existing example one line away, and the CLI test file shows exactly how to assert it. A good introduction to how flags, config resolution and the
--jsoncontract fit together in this repo. Comment here if you would like a pointer on the config layer part, which is optional.