Tuning the guardrails
Every limit and gate lives in orion.json at the repository root, reviewable
like code. Every key is in the
configuration reference.
The main settingsβ
| Setting | Default | What it stops |
|---|---|---|
limits.max_tool_calls | 400 | a session burning budget indefinitely |
limits.max_repeat_identical | 4 | the same call looping with no new input |
limits.max_consecutive_failures | 3 | retrying a broken thing forever |
limits.max_same_command_failures | 3 | one command failing over and over |
limits.max_session_minutes | 90 | wall-clock runaway |
limits.max_edits_without_verify | 25 | edits piling up with no passing test or build |
limits.max_files_touched | 60 | blast radius: a change too wide for one session |
limits.max_concurrent_tickets | 4 | how many tickets orion watch works at once |
limits.no_progress_minutes | 60 | a watch cycling all night without achieving anything |
qa.max_rounds | 3 | QA and the developer arguing until the budget runs out |
ci.auto_fix | true | a red build going straight to orion-failed instead of back to the agent; false turns the CI fix loop off |
ci.max_fix_attempts | 3 | an agent re-fixing a red build all night |
collect.batch_integration | false; true for projects orion plan creates | one pull request and CI run per ticket |
gates.require_plan_before_edit | false; true for projects orion plan creates | implementation before a written plan |
gates.protect_tests_during_fix | true | an agent weakening the test that defines "fixed" |
gates.production_requires_authorization | true | an unauthorised production deploy |
vcs.protected_branches | [main, develop] | direct pushes to either long-lived branch |
vcs.work_branch β vcs.default_branch | enforced | Orion merging agent work straight into the release branch |
What each breaker limit does when it trips is on Circuit breakers.
Set them with the commandβ
orion config limits # every bound, with provenance
orion config limits qa.max_rounds 3
orion config limits ci.max_fix_attempts 3
orion config collect # how work lands, and what each switch costs
orion config collect batch_integration true
orion config limits lists every bound with its effective value and where
that value came from: orion.json or the shipped default. With a key and
value it writes one. It edits the file the runner actually reads, which is
not always the one you are standing in, and it leaves the _comment_* keys
where they are. The two fix-round ceilings take their block as a prefix
(qa., ci.), because that is where they live in the JSON.
The web dashboard's Settings β Limits page runs the same checks, but refuses a value that the terminal would ask you to confirm.
Zero restores the defaultβ
A limit of 0 restores the default; it does not mean unlimited, because in
JSON a 0 is indistinguishable from a value nobody set.
The weekly budget is the one exception, and it goes the other way: there, zero means no budget. See Budget and monitoring.
The two fix-round ceilingsβ
qa.max_rounds and ci.max_fix_attempts bound how many times an agent is
handed what went wrong and asked to try again. Both default to 3.
Stopping too early sends a person work that one more exchange would have
finished. Each extra round raises the worst case on every ticket that fails
to converge, and that spend lands on the implementer, the agent that
dominates per-ticket cost. Set them to 2 to spend less.
In the CI loop, an identical repeated failure ends the loop immediately, so a third attempt is only reached by a run producing a different failure each round. Raising the ceiling does not change that.
Neither has an upper clamp. For a value above 5, orion config limits
states what it costs and asks you to confirm.
Concurrencyβ
limits.max_concurrent_tickets bounds how many agents run at once. It
defaults to 4. Set it to 1 for a strictly sequential watch.
Concurrent git against one shared clone, a budget checkpoint crossed by runs already in flight, several sessions hitting one rate limit, and tickets that all edit the same files are all absent at one and grow with the number in flight; conflicts grow roughly with its square. A value above 10 asks you to confirm, because these depend on a machine, a repository and a rate limit Orion cannot measure.
Two things to know:
- It multiplies with
limits.max_concurrent_children, the cap on how many subagents one run may fan out to at once (default 2). Four tickets each fanning out is a different load from four processes. - Approvals do not parallelise. N tickets finishing means N approvals waiting on one person.
Other limitsβ
These are in code and in the configuration reference, with shipped defaults you will rarely need to change:
| Setting | Default | Bounds |
|---|---|---|
limits.max_consecutive_polls | 12 | an agent polling a background command with nothing else happening |
limits.max_concurrent_children | 2 | subagents one run fans out to at once |
limits.max_breaker_trips | 2 | breaker trips before the planner evicts a ticket |
limits.max_stranded | 2 | times a ticket's worktree fails to settle before it is evicted |
The branch modelβ
Orion's job ends when work merges into the integration branch
(vcs.work_branch, develop). Promotion to the release branch
(vcs.default_branch, main) is a person's decision, so setting the two
equal is refused at config load, at orion init and by orion doctor. The
only way round it is the named opt-in vcs.allow_release_branch_merges. See
Adopting an existing repo.
Delegation and its costβ
Some delegated reviews are expensive. /security-deep-review commonly takes
15 to 30 agent calls, and the breaker counts every one against the 400-call
budget. Two settings in the delegation block handle that:
| Setting | Default | Effect |
|---|---|---|
delegation.extra_tool_calls_for_review | 200 | widens the tool-call envelope while a delegated review runs |
delegation.deep_security_review_when | high-risk | runs the deep review only when a change touches high-risk paths; always or never otherwise |
See nj-agents and other toolkits.
When a limit is wrongβ
If a limit is wrong for a repository, change its value in orion.json. Do
not work around the trip itself, since that leaves the breaker with no
effect.