> ## Documentation Index
> Fetch the complete documentation index at: https://taskgraph-ai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Loop Cost and Models

> What each loop iteration costs, how to cap a run, how a task's complexity picks its model and effort, and when loop reuses a Claude Code session

## Cost and caps

Every iteration reads what Claude Code reported spending, in dollars, and how many turns it took. Each iteration's status line shows them, with its duration, and the summary line ends with the run's total:

```
Iteration 1 of 2
✓ Iteration 1 · $0.42 · 12 turns · 3m 10s
Iteration 2 of 2
✓ Iteration 2 · $0.31 · 9 turns · 2m 4s

Iteration limit reached · 2 tasks completed in 2 iterations · $0.73 · 21 turns
```

The cost is Claude Code's own estimate (`total_cost_usd` in its `result` event), which can differ from your bill. To read it, each iteration runs `claude -p` with `--output-format stream-json`, with or without `--verbose`; an older Claude Code that prints no `result` event still works, without the cost.

Two flags cap a run:

* `--max-turns <n>` passes `--max-turns` to every `claude -p`. An iteration that hits it stops with an error, `Claude Code reached the --max-turns limit`, shown on its status line and in the progress file. Three in a row end the run like any other errors, and so does one on the last iteration (exit code 1).
* `--max-budget-usd <amount>` caps the whole run. Each iteration's `claude -p` gets what's left as `--max-budget-usd`, and the loop stops before an iteration once less than $0.25 is left (an iteration spends about that just reading its context), with the summary `--max-budget-usd reached` and exit code 0. Claude Code checks the cap against its own estimate and can go slightly past it. An iteration that reports no cost (stopped by loop's timeout, or killed or crashed after it started but before its `result` event) counts as the whole budget it was given, shown as `cost unknown, counted as $X`, so the run stops rather than overshoot. That run ends with status `error`, since the iteration failed, not `--max-budget-usd reached`. Without `--max-budget-usd`such an iteration shows`cost unknown`and adds nothing to the run's total, which then reads`at least \$X`. A `--parallel`task stopped or killed before its`result\` event shows the same way.

```bash theme={null}
taskgraph loop --max-turns 60 --max-budget-usd 10 --trust
```

Both work with `--sandbox` and `--parallel` (see [Run tasks in parallel](https://taskgraph-ai.com/capabilities/loop-parallel.md)).

### What each task cost

Each iteration also adds its cost and duration to the task it started on, so `taskgraph show` tells you what a task has cost across every run:

```
Task 3  Rate-limit the sign-in endpoint
✓ done · high priority · complexity 7 (high) · depends on 2
3 loop iterations · $1.24 · 12m 5s
```

The task is the one `--id` names, or the one `taskgraph next` picked when the iteration started; an iteration on a subtask counts on its parent task. A `--parallel` task counts its one Claude Code run the same way. With `--merge`, the Claude Code run that resolves the task's merge conflict adds its cost and time to the task too, but not an iteration. An iteration whose Claude Code never started (not installed, say) adds nothing. One that reported no cost adds its time and the budget it was given, if any; a `--parallel` task that reported no cost adds only its time. Presets that pick their own work, such as `linting`, record nothing.

`show --json` (and `get_tasks` with `fields: 'spend'`) returns it as an optional field, `"spend": { "usd": 1.24, "durationMs": 725000, "iterations": 3 }`. Only loop writes it: AI commands never see it in their prompts, and `update-task`, `update` and `expand` keep it. The [merge driver](https://taskgraph-ai.com/capabilities/merge-driver.md) merges it like any other field: when two branches both ran loop on a task, its `spend` is a clash for you to settle, since with squash merges and rebases the driver can't tell new spend from spend already counted.

## Model and effort by complexity

When the task an iteration works on has a complexity score (1 to 10), `loop` picks the model and effort for that iteration from it, so a one-line fix doesn't cost as much as a refactor. The task is the one `--id` names, or the one `taskgraph next` picks with the `default` and `tdd` presets. It passes `--model` and `--effort` to `claude -p`, which take precedence over the `model` and `effortLevel` in your Claude Code settings:

| Score | Model | Effort |
| - | - | - |
| 1-3 | `sonnet` | `low` |
| 4-6 | Claude Code's default | Claude Code's default |
| 7-10 | Claude Code's default | `high` |

`parse-prd` gives each task it writes a score, so a freshly planned project (including one `taskgraph go` planned) gets these bands with no extra step. A report from [`analyze-complexity`](https://taskgraph-ai.com/capabilities/cli-planning-commands#analyze-task-complexity) wins over that score for the tasks it covers. Each iteration reads the score as it is then, so a task re-scored during the run (by `scope` or `analyze-complexity`) runs at its new band from the next iteration.

A task without a score (a subtask, a task added some other way that `analyze-complexity` hasn't scored, or a preset that picks its own work such as `linting`) runs as before, with Claude Code's own model and effort. The status line and the progress file show what each iteration ran at, next to its cost:

```
✓ Iteration 1 · sonnet, low effort · $0.12 · 6 turns · 48s
```

```
# Iteration 1: success · complexity 2 · sonnet, low effort · $0.12 · 6 turns · 48s
```

`--model <model>` runs every iteration on that model (an alias such as `opus`, or a full model name). It replaces the band's model only; the band's effort still applies.

Set your own bands under `loop.bands` in `.taskgraph/config.json`. A task goes into the first band whose `maxScore` its score doesn't exceed; a band without `model` or `effort` leaves that one to Claude Code. `effort` is one of `low`, `medium`, `high`, `xhigh` or `max`:

```json theme={null}
{
  "loop": {
    "bands": [
      { "maxScore": 4, "model": "haiku", "effort": "low" },
      { "maxScore": 8, "model": "sonnet", "effort": "medium" },
      { "maxScore": 10, "model": "opus", "effort": "high" }
    ]
  }
}
```

A score above the last band's `maxScore` runs with Claude Code's defaults. `"bands": []` turns the picking off. A band `loop` can't use (a `maxScore` that isn't a whole number from 1 to 10, two bands with the same `maxScore`, an unknown effort, a model name that starts with `-`) stops the run before Claude Code starts, with the band named.

## Session reuse

Each iteration starts a new `claude -p` session by default, which re-reads `CLAUDE.md` and explores the repo again before it starts on the task. `--reuse-session` resumes the previous iteration's session instead (`claude -p --resume <session-id>`), so the next task starts with what the last one learned. Every API call in a resumed session carries the earlier task's history too, so it can cost more than it saves; compare the spend of a run with and without it on your project. With `--reuse-session`, the status line and the progress file say which each iteration did, `resumed` or `fresh`, and a resumed iteration's prompt opens by saying a new iteration starts.

Even with `--reuse-session`, an iteration starts a new session when:

* the previous iteration failed, timed out or reported no session
* the previous iteration changed no task's status and made no commit, or said all tasks were done while one was still ready
* the previous session's context reached `loop.resumeMaxContextTokens` (default 100000), read from the input, cache-write and cache-read tokens of its last API call
* the task runs at a different model or effort than the previous one (see [Model and effort by complexity](#model-and-effort-by-complexity))
* the run uses `--sandbox`, whose sessions stay in the container; `--parallel` tasks always start fresh

When Claude Code can't resume the session, the iteration runs again in a new one. The cost shown for a resumed iteration is what that run spent, not the session's running total. To change the limit, or turn resuming off with `0`:

```json theme={null}
{
  "loop": { "resumeMaxContextTokens": 60000 }
}
```

A value that isn't a whole number of 0 or more stops the run before Claude Code starts.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.