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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
events:
user_message:
- dispatch(worker)
worker_return:
- deliver_result
worker_flush:
- dispatch(compactor)
compactor_return:
- land_compaction
branch_stopped:
- mark_abandoned
- notify_ui
# Intermediate compaction checkpoints (ARCH §2.6–§2.7, §6). The executor
# reads this at each step boundary: when the trigger fires it dispatches a
# compactor off the compaction point — the branch tip, or `HEAD~keep_recent`
# when `keep_recent` is set — and the compactor's return lands by
# rebase-forward (the `compactor_return: land_compaction` binding above):
# the span before the point squashes into a compaction base and the live
# tail replays on top, zero downtime. `trigger` is one of
# `every_n_commits`, `every_t_seconds` (both take `n`), `window_percent`
# (`n` = the percent of the model's context window the branch's last usage
# fills, 1..=100 — the provider's own numbers, both of them: the prompt
# side of the newest model entry's `usage` report against the
# `context_window` the same report carries), or `on_flush` (the
# agent-elected `flush`, no `n`). `keep_recent` (optional, default 0) keeps
# the most recent commits out of the span; it must stay below `n` under
# `every_n_commits`. `keep_recent_tokens` states the same retained tail in
# the provider's own unit instead — keep the longest stretch of the
# transcript that cost at most that many prompt tokens to append, read off
# successive model entries' `usage` reports, no tokenizer and no stored
# counter. One tail or the other, never both: declaring both is refused at
# load. `extract_bytes` (optional) caps the extract the landing itself
# derives — `summary/<NNN>.refs.md`, the verbatim user messages, error
# strings, pull-request numbers, commit shas and paths the compaction
# takes out of context, written by code beside the compactor's prose
# (docs/DESIGN_CONTEXT_ECONOMY.md §5.3); omit it and no extract is
# written. Omit the whole block and the branch never compacts.
#
# The window is on the wire (brazen 0.0.9 stamps `context_window` onto
# every usage event), but the shipped trigger stays `every_n_commits`: a
# model whose provider row states no window is *declined* under
# `window_percent` rather than left silently never-due, so defaulting to
# it would refuse those workspaces at their first boundary. That is not a
# hypothetical any more — the survey ran (bl-4c64) and refused the flip.
# Of brazen 0.0.9's eight built-in rows exactly ONE can state a window at
# all (`google`, whose protocol names `inputTokenLimit`); the rest name no
# key to lift one from, `anthropic` and `claude-code` among them. And even
# that one states nothing until `bz --list-models` has run for it, because
# the window is read off the local model cache and the data plane's own
# writer stores a bare id with no metadata. So the variant is available,
# not defaulted: turn it on here when your row states a window, and see
# docs/ARCHITECTURE.md's bl-4c64 shipped-state note for what would make it
# the default (a change in brazen's rows, not in litany).
#
# `keep_recent_tokens` is unset here for the same reason and one more: a
# token tail under a COMMIT clock leaves a stretch where the clock is over
# threshold and the span keeps coming back empty — the branch re-walks its
# transcript at every step boundary and compacts nothing until the tail
# outgrows the budget. That is the shape `keep_recent >= n` is refused for,
# in units no load-time check can compare. The token tail belongs with the
# window trigger; they flip together or not at all.
#
# `n: 60` and `extract_bytes: 8192` are chosen together with the
# `tool_output:` bound below, because the clock counts commits and what
# a commit COSTS is that bound (bl-ce09). A step writes two commits, so
# 60 is about thirty steps. At the shipped 4 KiB-per-result bound a
# tool-heavy step appends a few thousand prompt tokens, so a span of
# thirty steps is tens of thousands — inside every shipped model's
# window, with the retained tail and the pinned head on top. The
# previous `n: 20` fired every ten steps whatever the context held: on
# the measured goals an ordinary conversation compacted six times, and
# each compaction is a model dispatch carrying the whole inherited
# transcript, so the clock cost more than the context it was reclaiming.
# The rule the pair is picked under is: an ORDINARY conversation should
# finish without compacting once, and a long one should compact rarely
# rather than continuously.
#
# `extract_bytes` moved with it for a reason that is not symmetry. Since
# bl-2071 the landing sweeps the span's transcript entries itself, so
# the extract is now derived from the whole span rather than from
# whatever a model happened to nominate — it will actually fill toward
# its cap, and unlike a tool result it stays in context until its
# summary is shed. 8 KiB is about two thousand tokens of references per
# compaction; 32 KiB was a cap nothing reached before and would now be
# paid every time.
compaction:
intermediate:
trigger: every_n_commits
n: 60
extract_bytes: 8192
# Harness-owned retry policy for a step's model call (ARCH §2.10, §4.4):
# brazen never retries — the harness re-invokes `bz` on a retryable
# in-band Error, up to max_attempts, with exponential backoff.
retry:
max_attempts: 3
backoff: exponential
# Bounded transcript projection of tool output (ARCH §3.3, §6). Each
# stream of a tool result (stdout and stderr independently) is bounded
# to its first head_bytes and last tail_bytes before the result envelope
# is rendered; the omitted middle is replaced by a marker stating the
# original byte/line counts and where the full record lives
# (steps/<agent-id>/<NNN>/tools/<tool-id>/output.json — always complete).
# Counts are bytes, never tokens. Omit the block and tool output reaches
# the transcript unbounded.
#
# 2 KiB + 2 KiB is the shipped bound, and it is small on purpose
# (bl-ce09). The number that ships is the one an ordinary conversation
# pays on EVERY step for the rest of its life, so it is chosen against
# the ordinary case and not against the rare one that wants the whole
# capture. At 4 KiB a result is roughly a thousand tokens: a `--help`,
# an `ls -la`, a `git status`, a test summary all land whole or land
# with their two useful ends and a marker between them. The previous
# 16 KiB + 16 KiB made ONE result worth about eight thousand tokens —
# measured, three ordinary calls filled a context, one `find` over a
# home tree put 32,985 bytes into a transcript essentially whole, and a
# conversation reading a repository reached 122,000 prompt tokens by its
# eighth step on `cat` output alone.
#
# What it costs is real and is priced here: a source file read whole is
# cut in the middle, and the model gets the marker instead. That is the
# intended trade — the full capture is on disk, the marker names its
# path and the byte and line counts, and re-reading a named range
# (`sed -n '120,180p'`) costs one cheap tool call, where carrying every
# whole file forever costs every later step. Raise both numbers on a
# workspace whose work really is reading long files end to end; that is
# what a severable policy block is for.
tool_output:
head_bytes: 2048
tail_bytes: 2048
# Context files (ARCH §3.3 *Context files ride the next tool result*,
# `docs/DESIGN_CONTEXT_ECONOMY.md` §6). File NAMES, looked for in every
# directory on the path from the enclosing repository's top level down to
# the agent's working directory. Each one the agent has not been shown
# yet is appended to its next tool result, framed <file path="..."> and
# bounded by tool_output above as its own stream; "already shown" is read
# off the transcript, so a compaction that drops the entry shows the file
# again. Omit the block and nothing is discovered.
context_files:
# Tool control (ARCH §3.3 *Tool control*, §6): an adjudicator binary
# consulted before every granted tool invocation executes — it answers
# pass, refuse, or hold (park for out-of-band review). Deliberately not
# configured here: no control ships, and omitting the block leaves the
# tool window unchanged. To wire one:
# tool_control:
# command: /path/to/control
# Whole-tree spend limits (ARCH §6 "Budgets (v0.7)"). One frozen ceiling
# for the whole agent tree, not a per-agent allowance: every driver in
# the tree — root or subagent — checks the tree's total against these
# same numbers, and a dispatch inherits no fresh budget. Checked at
# every model-call boundary before the adapter is invoked; spend, wall,
# and depth are derived from disk each check — no stored counter. Omit a
# limit (or the whole block) to leave that axis unbounded.
#
# Deliberately not configured here (operator ruling 2026-08-16): a
# whole-tree ceiling binds far earlier than its number reads, because a
# root and every agent below it spend one shared allowance — an hour of
# accumulated wall across a tree ends a conversation that is working. So
# nothing ships bounded: tokens, wall and depth are all unbounded, and a
# ceiling is config an operator adds. To wire one:
# budgets:
# max_total_tokens: 2000000
# max_wall_seconds: 3600
# max_depth: 4