nextv2.0.0
Concepts

Caps and budgets

How Kraft bounds time, tokens, and dollars, why a raise is bounded, and how a policy resolves from policy.yaml down to a single task.

Kraft stops a work item that runs too long or spends too much, and it hands the item to you instead of failing it. This page explains how those bounds fit together and why they work the way they do. For every key and its default, see policy.yaml. To raise a cap that stopped an item, see Raise a cap on a stopped item.

The default caps

These are the limits a fresh install runs under.

BoundShipped valueWhere it is set
Fix-loop attempts2 for verification, 3 for merge_request_feedbackmax_attempts on the loop, in the chain
Fix-loop attempts, for a loop that sets none3default: attempts in policy.yaml
Fix-loop wall clock3600 secondsdefault: wall_clock_s in policy.yaml
Work item dollar cap10budget.work_item_usd in policy.yaml
Daily dollar cap50budget.daily_usd in policy.yaml
implementer task running time120 minutestime_cap_minutes on the task, in library.yaml
Wait for CI90 minutestotal_time_cap_minutes on the wait task, in library.yaml
Wait for the automated review30 minutesthe same key
Wait for an external approval10080 minutes (7 days)the same key
Wait for the post-merge pipeline90 minutesthe same key
Cap per level of scope (time_cap_minutes, total_time_cap_minutes, token_budget, budget_usd)nonedefaults: in policy.yaml
Maximum per level of scopenonemaxima: in policy.yaml

Whichever bound runs out first stops the item, and the stop names its scope.

The shipped policy.yaml has example per-level defaults, commented out. If you uncomment them, the work item runs under 480 minutes, each node under 180, each step under 120, and each task under 90, all at once.

Two kinds of bound

Kraft bounds work in two ways.

Loops bound repetition. A fix loop retries a node until it passes. Its attempt limit is max_attempts in the chain (attempts under loops: and default: in policy.yaml). Its wall clock is wall_clock_s there.

An external wait (a pipeline, an automated review, an approval, a merge landing) is not a loop. Its timeout is its own task's total_time_cap_minutes.

Caps bound a scope's resources: time, tokens, and dollars. A scope is one of four, each inside the one before:

  • the whole work item;
  • one node, a stage of the item's chain;
  • one step, a group of tasks inside a node that run in parallel (a node's steps run in order);
  • one task, a single unit of work, such as one agent run or one command.

Only a person resets a loop's count. A retry from the board or from kraft item retry starts the fix loops of the retried span afresh. A retry that a worker, an MCP assistant, an escalation turn, or a rate-limit relaunch asks for keeps counting. An escalation turn is the agent Kraft starts to help when an item stops and waits for you. See Fallback and escalation.

A gate rejection is the one exception, and only for fix loops. The nodes it re-runs measure new work, so their fix loops start afresh whoever rejected. Each rejection still counts against the gate's own reject loop, which only a person resets.

Why loops: ships empty

A loops: key that names no live loop does nothing and warns nobody. A stale cap would look live and bind nothing, so the shipped policy.yaml names none.

The default: entry covers every fix loop. A loop's own max_attempts in the chain wins over default:. The shipped fix loops therefore take their attempts from the chain and only their wall clock from default:.

Caps are set per level

You set the four caps (time_cap_minutes, total_time_cap_minutes, token_budget, budget_usd) per level of scope: the work item, its nodes (a gate is a node), its steps, and its tasks.

A level's default binds every scope of that kind that nothing set a value for. Out of the box no level default is set. Only the caps the shipped tasks set on themselves in library.yaml apply, alongside the budget: dollar caps.

A value set in the chain (on the chain, a node, a step, or a task) or by the item's own override replaces the defaults for that scope and for every scope under it that sets none. A node set to 240 minutes runs its steps and tasks under 240 too.

A default is not a ceiling. Any scope may set more, up to its level's maximum. A narrower level's cap may not exceed a broader one's (tasks <= steps <= nodes <= work_item), so a task cap of 20 minutes inside a step capped at 10 is refused when the chain loads.

A cap in a repository's policy: (in repos.yaml) or in the chain's own policy: is a cap on the work item as a whole.

How a policy resolves

A work item freezes its policy when you file it. Kraft layers it from the broadest source to the narrowest:

  1. defaults and maxima in policy.yaml.
  2. The repository's policy: in repos.yaml.
  3. The chain's policy:.
  4. Each node's, step's, and task's own policy:.
  5. Your item's own override, from kraft item create --policy or kraft item set-policy.

At intake, Kraft refuses a layer that relaxes what it inherits, and names the scope. A bad policy therefore fails when you file the item, not hours into a run.

Your override comes last, so its operational values win over the template's. A max_attempts or timeout_minutes on an execution node wins over its fix loop's own max_attempts.

An override's safety values only ever tighten:

  • An allowed_tools list intersects with what the scope already allows.
  • A grants list keeps only the grants the scope already holds.
  • deny_tools adds to the list.
  • A sandbox must match any already set.

The chain narrowing the same field first never causes a refusal.

Your override is the one layer that can change after filing. On a running or waiting item, a change takes effect from the next node entered and the next check of a wait.

A recovery plan inherits the task, step, or node that declares it. A fix loop, its judge, a node's escalation task, and its conflict handler inherit their node. A gate's auto_review inherits its gate.

Time caps

There are two clocks.

  • time_cap_minutes counts running time. Paused time, external waits, gates, and rate-limited time do not count.
  • total_time_cap_minutes counts wall-clock time, including waits, gates, and rate limits, less only a manual pause. For a task other than a wait, it is the task's running time. For a wait, it is the wait's timeout.

Every clock counts from the scope's start in the current run. A person's retry starts them all afresh. A rate-limit relaunch or a stuck escalation's own retry does not.

A node's clock is its own, independent of its fix loop's timeout_minutes, which bounds only the fix cycles. A node's cap covers everything that runs as that node: its tasks, recovery, fix loop, a gate's automated review, and the automatic stuck-escalation turn. Your own escalation chat is not the node running, so it is not capped.

Hitting a cap stops the item and waits for you, with the reason "<scope> hit its time cap of N minutes". The reason reads "total time cap" for total_time_cap_minutes, and the scope reads "the work item" for the work item itself.

A time cap is never treated as a code failure. It spends no recovery or fix-loop attempt, and no stuck escalation answers it. A gate's own timeout stops the item the same way, naming the gate, and the gate stays open for you to approve or reject.

A work item's total_time_cap_minutes counts its gates and external waits, so a cap of two days stops an item still waiting on a week-long approval. Size it for the longest wait your chain allows.

Token and dollar budgets

A token or dollar cap refuses to start the next agent launch once its scope, or any scope around it, has reached its cap. Kraft checks before each launch, gate reviewers and escalation turns included. It cannot interrupt a running agent, so overshoot is bounded by one launch's cost.

While an agent is still running, its spend counts too. Kraft prices the agent's known tokens against a shipped rate table and shows the result as an estimate (~$4.20 (est.)) until the session exits and the agent's own reported figure replaces it. A long session's spend can therefore trip a dollar cap before the session finishes, not only after.

Three things follow.

  • A finished launch whose harness reported no cost counts at an estimate from the same rate table. If Kraft cannot price it, the caps differ: see Harnesses that report no cost.
  • Launches that start together in one scope, such as a parallel step's tasks, each pass the check before any of them has spent. A shared scope's cap can be overshot by up to their combined cost.
  • A launch that resumes an earlier agent session counts only what it added.

budget.work_item_usd and budget.daily_usd in policy.yaml stay the outer ceilings, above any per-scope budget_usd.

For which harnesses report no cost and how Kraft estimates what they spent, see Cost reporting.

Why a raise is bounded

You can raise the cap that stopped an item for that one item, and retry it. The raise is bounded so that an item cannot pass the maxima you set for every item.

  • An item's own cap may exceed the chain's, but only up to maxima.work_item.
  • A cap on a node, step, or task may pass its level's default, up to its level's maximum, and never what an enclosing scope allows.
  • A limit set outside the item, such as budget.daily_usd, is not the item's to raise.
  • On spend a harness never reported, no higher number helps, because Kraft still cannot show the scope is under the new cap. Only lifting the dollar cap with --usd none passes it.

The steps are in Raise a cap on a stopped item, and the command's refusals are in Raise a cap that stopped an item.

The shipped implementer cap

The implementer task, which both shipped chains run, sets time_cap_minutes: 120. Successful implementer runs finish well inside that time, so the cap binds only a run that has already gone wrong, for example a worker stuck waiting on a test run it backgrounded.

A turn that ends with a background job still running fails the task: see Result file.

A long plan can need more than 120 minutes. Raise the item's own cap, or set a larger one on the task in library.yaml. The task sets 120 minutes, above the commented example's tasks default of 90, which a scope may do. A maxima.tasks.time_cap_minutes (or a broader level's) below 120 makes Kraft refuse both shipped chains at intake, so lower the task's cap to fit.

Why maxima start unset

A fresh install ships with defaults: and maxima: commented out. An uncommented value is a real ceiling, and Kraft must not silently cap someone who never chose a number.

A safety field listed only in maxima, such as allowed_tools, starts at its maximum and can only be narrowed. That is why the reference example sets no maxima.allowed_tools: a list there binds every agent task on every harness.

Not every harness can enforce a tool list. On claude, the harness every shipped task uses, a list removes every built-in tool it does not name. Other harnesses enforce it differently or refuse to launch under one. See Permission gate for each harness, and policy.yaml for the web tools codex and cursor need in the list.

Copyright © 2026