1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# magi competing on its own repository.
#
# **Only what is peculiar to this checkout lives here.** The roster, the seat
# assignment, the shape of a run and the update policy moved to the machine
# layer (`<config_dir>/magi/config.toml`), because none of them are facts about
# this repository: they are facts about which agent CLIs are installed and what
# the operator is willing to spend. Stating them once there is the point of
# having layers, and it keeps this file to the things a different machine would
# still need to be told.
#
# What that leaves, and why each one has to be here:
#
# - `[verify]` runs `cargo`. A repository that is not Rust cannot inherit it.
# - `[merge] base` names this repository's branch, and is required at all
# because of how this checkout is set up (see below).
# - `[prompts]` are this project's conventions.
#
# Note that an array may be declared in exactly one layer
# (`Config::refuse_split_arrays`), so `[verify]`'s arrays being here is also
# why the roster's arrays are *only* over there.
#
# Rendered by teravars. Tera braces are live everywhere in this file, but
# comments are stripped before Tera sees them (teravars >= 0.2.2), so a comment
# may quote `{{ ... }}` freely.
[]
# Shared build cache for the verification commands. Override per machine with
# MAGI_CACHE. Lives beside its only consumer: `[vars]` is rendered per file, so
# a value used by this file's templates is stated in this file.
= "{{ env.MAGI_CACHE | default(value='/tmp') }}"
[]
# The real-machine leg, run in the winner's worktree once per review round.
#
# CARGO_TARGET_DIR is shared on purpose: each candidate worktree would otherwise
# compile this crate from scratch, which is by far the slowest thing in a run on
# a Rust repo (minutes, against ~90 s of agent latency per node). Only the
# winner's worktree runs these commands, and only one at a time, so nothing
# contends on cargo's lock.
= ["CARGO_TARGET_DIR={{ vars.cache }}/magi-target cargo test --locked --all-targets"]
# Same gate as CI and the pre-push hook.
= ["CARGO_TARGET_DIR={{ vars.cache }}/magi-target cargo make check"]
# Run just before the gate, on the winner only. Run 892b was blocked by nothing
# but a one-line rustfmt wrap; a formatter is deterministic, so run it rather
# than argue about it with an agent. Changes land as one commit.
= ["cargo fmt --all"]
[]
# `base` is set explicitly because this is a jj-colocated checkout: jj keeps
# git's HEAD detached at the working-copy commit, so the `git symbolic-ref
# HEAD` fallback in `git::current_branch` would fail here. Teaching magi to
# read the jj bookmark instead is a real feature, not a workaround — tracked
# as its own competition. That fallback only runs when `base` is unset,
# though: with `base` given, `config.merge.base` wins over the fallback no
# matter what `mode` is, so the inherited `mode = "pr"` never reads HEAD — the
# `Pr` arm in `land.rs` just pushes the winner's worktree and opens a PR
# against `state.base_branch`. The only mode that depends on HEAD is
# `mode = "local"`, which merges directly in the primary worktree and needs
# to confirm the checked-out branch matches `base` first. Run
# 20260903-134458-ec12 landed this repository's own PR with `--mode pr` as a
# CLI override, confirming this in practice.
= "main"
# This repository's own GitHub ruleset on `main` requires linear history
# ("must not contain merge commits"), so the manual-merge guidance `magi
# show` prints must not be `git merge --no-ff` — that push is rejected for
# anyone without bypass rights. `squash` matches what `[graph] land` already
# runs for `mode = "pr"` (see `land.rs`'s module doc).
= "squash"
[]
# `cargo make check` on this repository takes ~1450s on a warm cache on this
# machine — measured, not guessed — against a `timeout_review` default of
# 1200s, which `timeout_verify` inherits when it is unset. That made the gate
# fail structurally rather than because anything was wrong: run
# 20260915-215701-c1c0 and run 20260915-225246-6805 both reached
# `verify.gate` with two approvals and a green e2e, then timed out at 1200s.
# The e2e leg has the same problem for a different reason: `verify.e2e` is
# `cargo test --all-targets`, which is the same build plus the same suite.
#
# Stated here rather than on the machine layer on purpose: how long this
# repository's own gate takes is a fact about this repository (a Windows box
# building several agent worktrees at once), not about the operator's
# preferences. 2400s is ~1.6x the measured time, which is headroom for a
# busier machine rather than a licence to hang.
= 2400
# `timeout_verify = 2400` above fixed `verify.gate` / `verify.e2e`, but
# `timeout_review` (the field it was split off from, see its doc comment in
# `src/config.rs`) was left at its 1200s default. That field is the timeout
# for the reviewer seat itself — both the ordinary review job
# (`src/graph.rs:2506`, stem `review-{round}-{n}`) and the reconsider-review
# job (`src/graph.rs:2689`, stem `review-{round}-reconsider-{n}`) pass it to
# `Duration::from_secs` directly. `timeout_verify` only bounds the real-machine
# verification commands, so raising it did nothing for the reviewer's own
# budget: `review-1` kept timing out every round on this machine, e.g. runs
# 20260919-213057-71fd, 20260919-152903-01b7 and 20260919-072215-fa6e.
#
# Set to the same 2400s as `timeout_verify` because both seats run on the same
# machine under the same load, not because the two fields are linked —
# `timeout_verify` still won't follow `timeout_review` if this changes again
# (that independence is the point of splitting them, see `src/config.rs`).
# 1200s was failing structurally every round, so the accepted cost here is the
# same one already paid above: up to 20 extra minutes to reclaim a reviewer
# that has actually hung, in exchange for one that hasn't finishing normally.
= 2400
# Project conventions appended to the node prompts, never merged into them.
# Repository-wide context lives in AGENTS.md, which every agent already reads
# from the checkout; these are the things a magi *node* needs to know.
[]
= """
This repository uses jj (Jujutsu) with a colocated git repo. `git commit` works \
in a candidate worktree and is what magi expects, but never run `jj` commands \
and never touch the main working copy.
Full verification is parent-owned: magi runs `cargo test --locked --all-targets` \
and `cargo make check` itself, once a round has no blocking findings left and \
again before landing. Do not re-run the full suite or the full gate yourself — \
a focused `cargo check` or a targeted `cargo test <name>` for what you touched \
is enough. Whenever you do build or test locally, set \
`RUSTUP_TOOLCHAIN=stable`: the environment here pins an older toolchain than CI \
uses, so a lint that fails CI is invisible without it.
"""
= """
Formatting is not a finding: `cargo fmt` runs in the gate. Spend the round on \
correctness, on invariants the code claims and does not keep, and on tests that \
assert plumbing rather than behaviour.
"""
# Context-window sizes (tokens) the chat's usage meter divides by, keyed by the
# model name an agent passes to its CLI. Merged over a built-in table; an exact
# name wins, then the longest prefix. A model with no entry shows a raw token
# count and no percentage. Example:
#
# [context_windows]
# "claude-opus-4-1" = 200000
# "my-local-model" = 32768