Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

What We Measure

Every number writ publishes is on this page, with the script or evidence file that produced it, the date, and the binary it ran on. A number without a file is not on this page and not in a release note. Misses stay on the page: the history is the point, not the latest result.

Two kinds of source:

  • Scripts live in this repository and anyone can run them: bench/release_checks/, bench/finish_audit.py, bench/writ-scorecard.sh.
  • Evidence files are the raw records of live runs: each agent’s full transcript, writ’s own records, doctor output before and after, and the finish audit. Paths below are relative to the evidence collection, one folder per run.

A live run is two Claude Code agents, launched with claude -p, working at the same time in one directory on a small project, on features that touch the same files. Nobody tells them about writ beyond what writ init generates. At the end the harness runs writ finish and audits the commit.

Zero touch safe finish

The question: at the end of a run, does writ doctor say the repository is safe to finish, with no human having run anything? Scored per agent; “2/2” means both agents left the repository in that state.

DateRelease treeRunResultEvidence
2026-10-060.4.0live run 10/20.4.0-live-run1/summary.json
2026-10-060.4.0live run 30/20.4.0-live-run3/summary.json
2026-10-060.4.0live run 40/20.4.0-live-run4/summary.json
2026-10-110.4.1live run 10/2 strict, 2/2 lenient0.4.1-live-run1/summary.json, 0.4.1-live-run1/rescore.json
2026-10-110.4.2run A, a claim left by an exited session2/20.4.2-live-runA/summary.json, report.md
2026-10-110.4.2run B, a spec committed under a working agent2/20.4.2-live-runB/summary.json, report.md
2026-10-120.4.2run B again, final build2/20.4.2-live-runB2/summary.json, report.md
2026-10-110.4.1 releasestranger kit on a real repository (677 commits)2/20.4.2-stranger-kit-e2e/summary.json
2026-10-140.4.3 development build 86a5d9c7d67dtwo agents, each splitting its task across two subagents; no writ instructions, sealing automatic; the receipt named the wrong author for four files (finding 169, fixed in 07209051b2d9)2/20.4.3-fleet-live-c409/summary.json, report.md
2026-10-160.4.3 release candidate 5da679fd4fab (Amis 3b14d8a540c3)exit run A: two agents, no writ instructions, sealing automatic; a hand seal over the other agent’s sealed lines was refused and the agent merged them back from the seal2/20.4.3-live-runA-freeze4/summary.json, report.md
2026-10-170.4.3 release candidate 5da679fd4fabexit run B: two agents and a claim left by an exited session, released by one agent with --stale; a hand seal over the other agent’s sealed line was refused and the agent merged it2/20.4.3-live-runB-freeze4-rerun/summary.json, report.md (earlier attempts on this build ended on the agents’ session limit and are kept as voids)
2026-10-190.4.4 release candidate 6a9d7c2aad2dexit run A: two agents, no writ instructions; three seals refused for dropping the other agent’s sealed lines, each merged2/20.4.4-live-runA-freeze6/summary.json, report.md
2026-10-190.4.4 release candidate 6a9d7c2aad2dexit run B: two agents and a claim left by an exited session, released by an agent without --force2/20.4.4-live-runB-freeze6/summary.json, report.md
2026-10-190.4.4 release candidate 6a9d7c2aad2dtwo agents splitting their tasks across named subagents (store-builder, web-builder); the receipt names them, and web-builder-ab037dfe for the second live instance2/20.4.4-fleet-live-freeze6/summary.json, receipt.txt

What the misses were. On 0.4.0 the fix for the injected stale claim needed --force, which an agent should not run on its own. On 0.4.1 both agents read the fix two or three times and left it alone because the claim was not theirs; “lenient” counts a finish that was safe once the human ran the printed fix. 0.4.2 changed the finding to say any agent may run it.

Run B2 has a void-429/ folder: an earlier attempt that stopped when the agents hit their session limit. It is kept and not scored.

The 0.4.2 runs used dev builds of the 0.4.2 tree, before the version bump, so their summary.json says writ 0.4.1; each report.md names the binary’s hash and the seal it was built from.

Sealed lines lost

The question: did the commit writ finish made keep every line any agent sealed? bench/finish_audit.py checks every added line of every seal against the finish commit. A line is either in the commit, replaced by a later seal, or lost.

DateWhat was finishedAdded lines checkedReplaced by a later sealLostEvidence
2026-10-060.4.0 live run 1914000.4.0-live-run1/audit.txt
2026-10-060.4.0 live run 3725000.4.0-live-run3/audit.txt
2026-10-060.4.0 live run 4697000.4.0-live-run4/audit.txt
2026-10-110.4.1 live run 11,046400.4.1-live-run1/audit.txt
2026-10-110.4.2 run A989000.4.2-live-runA/audit.txt
2026-10-110.4.2 run B1,062300.4.2-live-runB/audit.txt
2026-10-120.4.2 run B again729000.4.2-live-runB2/audit.txt
2026-10-11stranger kit, real repository100000.4.2-stranger-kit-e2e/audit.txt
2026-10-12writ itself, commit 68ee77f (60 files, four agents)10,228260audit_68ee77f.txt
2026-10-140.4.3 run with subagents (13 files, 7 specs); the receipt named the wrong author for four files (finding 169, fixed in 07209051b2d9)2,6946500.4.3-fleet-live-c409/audit.txt
2026-10-160.4.3 release candidate, exit run A (13 files, 3 specs; finish’s survival line: 1,079 sealed lines, none lost)1,896400.4.3-live-runA-freeze4/audit.txt
2026-10-170.4.3 release candidate, exit run B (17 files, 2 specs; finish’s survival line: 1,152 sealed lines, none lost)1,741300.4.3-live-runB-freeze4-rerun/audit.txt
2026-10-190.4.4 release candidate, exit run A (2 specs; finish’s survival line: 1,045 sealed lines, none lost)1,625000.4.4-live-runA-freeze6/audit.txt
2026-10-190.4.4 release candidate, exit run B (2 specs; finish’s survival line: 1,119 sealed lines, none lost)1,8702700.4.4-live-runB-freeze6/audit.txt
2026-10-190.4.4 release candidate, run with named subagents (5 specs; finish’s survival line: 1,485 sealed lines, none lost)2,275300.4.4-fleet-live-freeze6/audit.txt

Survival check time

The survival check (writ doctor --survival) is what decides whether writ will say “safe to finish”. Measured on the writ repository itself by bench/release_checks/doctor_timing.sh, which copies the repository and its .writ to a scratch directory and times doctor there, five runs each.

DateBinaryCommitFast tier medianSurvival, full runSurvival, cached
2026-10-14writ 0.4.2 (Homebrew)6f0d234150 ms4.0 to 4.8 s150 ms
2026-10-160.4.3 release candidate 5da679fd4fab6f0d234, store of 297 seals; load average 3.3 rising to 4.6 on 10 cores60 ms5.3 to 5.6 s58 ms

A full run is the whole store checked from scratch; it happens once after the store changes, and later runs reuse the result. Each run’s samples and conditions are saved in 0.4.3-doctor-timing/ from 2026-10-16 on. The full run grows with the store: about 5.5 s at 297 seals. The 0.4.2 release note said 3.4 to 4.5 seconds; those timings were recorded in development seals, not saved to a file, so this page shows the script’s measurement instead.

Hurdles

The question: how often does writ get in an agent’s way? A hurdle is a forced seal, an allowed removal, a refused seal, or a refused finish, counted from writ’s own records only (seal fields and refusal events), over the seals of the specs in one sprint. Produced by bench/writ-scorecard.sh --hurdles-records, with WRIT_HURDLE_SPECS naming the sprint’s specs.

ReleaseSpecsHurdlesSealsPer 10 sealsCounted
0.4.1stale-release, survival-tier, ci-and-hooks, harness-0-4-120414.92026-10-14
0.4.2act-on-it, merge-descendant, session-end-and-ci, harness-0-4-212383.22026-10-14
0.4.3auto-seal, harness-0-4-3, receipt-and-scale, release-0-4-3, close-0-4-3-agents, fix-0-4-3-build28883.22026-10-18

0.4.2 over 0.4.1: 0.65 times the rate. The 0.4.2 release note said 0.67 times (20 over 39, 12 over 35) because the seal totals were counted before each release’s own release seals; the hurdle counts are the same. 0.4.3 over 0.4.2: the same rate. 24 of its 28 hurdles are allowed removals in the test harness and auto seal specs, where fixtures were rewritten on purpose; 16 refusal events in the sprint name no spec of the set and are not counted. From 0.4.3 the count is saved to a file when the release is cut: 0.4.3-hurdles/hurdles_records_5d30ff7_20261018T071256Z.txt.

Unsealed window

The time from an agent’s edit to the seal that records it, read from each run’s hook log. writ takes the edit time from the file’s modification time, so the figure is exact; on a file system whose timestamps are too coarse to order the edit, the record says so and the figure is an upper bound (“at most”).

DateRunEdit pathEdit to seal, median / worstOverEvidence
2026-10-140.4.3 run with subagents (build 86a5d9c7d67d); the receipt named the wrong author for four files (finding 169, fixed in 07209051b2d9)shell commands (Bash); the run used no edit tool366 ms / 1.2 s14 seals of 9 files, none with a coarse timestamp0.4.3-fleet-live-c409/summary.json (window_mtime), from hooks.log
2026-10-160.4.3 release candidate, exit run Ashell commands (Bash) and Write258 ms / 260 ms3 seals, none with a coarse timestamp0.4.3-live-runA-freeze4/summary.json (window_mtime)
2026-10-170.4.3 release candidate, exit run Bshell commands (Bash)439 ms / 439 ms2 seals, none with a coarse timestamp0.4.3-live-runB-freeze4-rerun/summary.json (window_mtime)
2026-10-180.4.4 development build 8538885aee46, exit run Ashell commands (Bash)560 ms / 3,055 ms28 seals, none with a coarse timestamp0.4.4-live-runA-freeze5/summary.json (window_mtime)
2026-10-190.4.4 release candidate 6a9d7c2aad2d, exit run Ashell commands (Bash)416 ms / 2,993 ms25 seals, none with a coarse timestamp0.4.4-live-runA-freeze6/summary.json (window_mtime)
2026-10-190.4.4 release candidate 6a9d7c2aad2d, exit run Bshell commands (Bash)272 ms / 428 ms23 seals, none with a coarse timestamp0.4.4-live-runB-freeze6/summary.json (window_mtime)
2026-10-190.4.4 release candidate 6a9d7c2aad2d, run with named subagentsshell commands (Bash) 12, Write 2300 ms / 345 ms14 seals, none with a coarse timestamp0.4.4-fleet-live-freeze6/summary.json (window_mtime)

The 0.4.4 runs seal many more writes than 0.4.3’s, including writes early in long commands such as a test run, whose seal waits for the command to return; that is where the worst cases above come from. The edit tools have one live figure so far (two Write seals in the run with named subagents); their fleet figures are below.

Ambiguous changes

When two agents run shell commands at the same time and a file changes while both are running, writ cannot always tell whose change it is. It then seals the file for nobody and asks each of those agents in one line to claim it, because sealing a change under the wrong agent is worse than not sealing it.

RunIdentities in the run*Agents askedHook sealedNot attributed (records)Ask lines shownEvidence
c40964141740.4.3-fleet-live-c409/summary.json (ambiguity), from hooks.log
A2232850.4.3-live-runA-freeze4/summary.json (ambiguity), release candidate 5da679fd4fab
B32254100.4.3-live-runB-freeze4-rerun/summary.json (ambiguity), release candidate 5da679fd4fab
A on 0.4.42028000.4.4-live-runA-freeze5/summary.json (ambiguity) and ask_counts.json, 2026-10-18, development build 8538885aee46 with the PostToolUseFailure hook and command text attribution; PostToolUseFailure fired 7 times, 32 windows opened and 32 closed
A on 0.4.42025000.4.4-live-runA-freeze6/summary.json (ambiguity) and ask_counts.json, 2026-10-19, release candidate 6a9d7c2aad2d; 31 windows, all closed
B on 0.4.43023000.4.4-live-runB-freeze6/summary.json (ambiguity) and ask_counts.json, release candidate 6a9d7c2aad2d; 33 windows, all closed
named subagents on 0.4.46014000.4.4-fleet-live-freeze6/summary.json (ambiguity) and ask_counts.json, release candidate 6a9d7c2aad2d; 58 windows, all closed; one seal attributed from the command text

* Identities in the run: c409’s six are two mains and four subagents; run B’s third identity is the injected stale session, which never edited.

“Not attributed” counts hook log records, including the end of turn sweep, which prints nothing; “ask lines shown” counts the lines an agent actually received (finding 187, counted 2026-10-18 from the hook logs and transcripts, 0.4.4-ask-count/results.md). In these runs the agents sealed when asked and nothing was lost.

Where the records come from (finding 186, measured 2026-10-18 on these three runs): Claude Code sends the PostToolUse event only after a shell command succeeds; a failed command gets a separate PostToolUseFailure event, which 0.4.3’s hooks do not register. So a command that exits non zero, such as cat on a missing file, leaves that agent’s window open until its turn ends, and every file the other agent changes in that time has two candidates. All 99 records across the three runs have such a window among their candidates. 0.4.4 registers the failure event and closes stale windows; on the same three runs that leaves 6 records of 99 ambiguous, with 0 wrong attributions (0.4.4-ask-count/results.md, the “rules 0+1+2” column; the change ships when the two agent runs on the 0.4.4 build confirm it).

Credit moved: files sealed by an agent that the hooks had not named as the maker or a candidate. On the run above, 6: the four files of finding 169 and two sealed by a main agent whose candidates were its own subagent and the other main agent (0.4.3-fleet-live-c409/report.md). On the three 0.4.4 release candidate runs, 0 in each, and 0 wrong attributions, checked by hand against each identity’s own transcript (README.md and summary.json credit_moved in 0.4.4-live-runA-freeze6/, 0.4.4-live-runB-freeze6/ and 0.4.4-fleet-live-freeze6/).

Hook cost

WhatSizeResultEvidence
Snapshot taken before each shell command (Bash), per call50 identitiesnot measured yet

Fleet numbers

Many agents editing in one directory at the same time, driven through the installed edit hook by bench/fleet/sim.py, the way Claude Code calls it. Measured on 2026-10-15 on the 0.4.3 release candidate (binary sha256 5da679fd4fab, the tree of the 0.4.3 tag before its Windows build fix), and on 2026-10-14 on an earlier development build (c63f55be96f4, before the store lock stopped polling), against 0.4.2 with the same script. Reproduce with bench/release_checks/fleet_numbers.sh, which reruns each published shape and compares.

What these numbers cover. They are from the edit tool path (Edit, Write, NotebookEdit), where the hook is told which file the agent edited. Edits made by shell commands (the Bash hook pair) are not measured here, and “no edit sealed under the wrong agent” from these runs does not carry over to them.

On the release candidate, at one agent the seal follows the edit by 13 ms. At 50 agents in one directory: median 985 ms, nine in ten under 3.0 s, worst 6.5 s, every edit sealed by the end; the slowest hook took 682 ms to return to the agent. The development build before the lock change read 38 ms at one agent and a median of 5.0 to 5.3 s at 50, its rows kept below. Edit to seal is an upper bound: the time from the edit to the first seal that holds it.

Agents x editsRepositoryHook return p50 / maxEdit to seal p50 / maxEdits sealed by the endSealed under another agentEvidence
1 x 50, one after another, release candidate (load average 10 on 10 cores)clone of the writ repository, 279 files21 ms / 49 ms13 ms / 42 ms50 of 5000.4.3-fleet-sim/1x50-this-repo-freeze4.json
50 x 20, all at once, release candidate (load average 7.3 rising to 10.2)generated, 1,105 files383 ms / 682 ms985 ms / 6.5 s1,000 of 1,00000.4.3-fleet-sim/50x20-freeze4.json
1 x 50, one after another, development buildclone of the writ repository, 279 files90 ms / 152 ms38 ms / 77 ms50 of 5000.4.3-fleet-sim/1x50-this-repo.json
50 x 20, all at once (load average 3.8 on 10 cores)generated, 1,105 files295 ms / 937 ms5.3 s / 11.0 s1,000 of 1,00000.4.3-fleet-sim/50x20-run1-with-load.json
50 x 20, all at once, again (load average 3.5)generated, 1,105 files291 ms / 1,081 ms5.0 s / 11.0 s1,000 of 1,00000.4.3-fleet-sim/50x20-run2-with-load.json
0.4.2, 50 x 20, a seal command per editgenerated, 1,105 files7.0 s / 21.7 snot measured9 files left unsealed00.4.3-fleet-sim/before-0.4.2-naive-50x20-run1.json
0.4.2, 50 x 20, againgenerated, 1,105 files7.1 s / 20.1 snot measured17 files left unsealed00.4.3-fleet-sim/before-0.4.2-naive-50x20-run2.json

0.4.2 has no auto seal; its rows use a hook that runs writ seal --paths after each edit, the closest a user could get. At 50 agents in 0.4.3 most edits are sealed when each agent’s turn ends rather than on the edit itself (release candidate: 273 hook calls sealed on the edit, 206 put off because another agent held the store lock, 26 seals at the end of the turn; development build, second run: 19, 11 and 42), which is why the window there is the turn, not the hook.

These are not a pass or fail against the 0.4.3 aim of edit to seal under 1 s: that aim was set for the two-agent live runs above, not for 50 agents.

The floor. If a seal is put off because another agent held the store lock, and the session is killed before its turn ends, that file stays unsealed until writ finish reports it as left out. Nothing seals it sooner.

Seal cost

WhatBinaryp50 / maxEvidence
One writ seal --paths of one file on a clone of the writ repository, 15 runswrit 0.4.2165 ms / 188 ms0.4.3-fleet-sim/bare-seal-this-repo.json
Same0.4.3 development build c63f55be96f4123 ms / 146 mssame file
50 identities x 20 seals at once, generated repository of 1,056 files, writ seal run directly0.4.3 development build before the lock change (3947b702d852)7.9 s / 18.2 s; 480 seals failed on the lock timeout, 521 succeeded0.4.3-fleet-haris/fleet_50x20_before_3947b702d852.json
Sameafter (adbd8d70c276)837 ms / 7.2 s; 0 failed0.4.3-fleet-haris/fleet_50x20_after_adbd8d70c276.json

Release checks

Every claim in a release note has a script in bench/release_checks/ that tests it on a throwaway project. To reproduce a release’s count, build that release and run them all:

cargo build --release -p writ-cli
for s in bench/release_checks/*.sh; do "$s" || echo "FAILED: $s"; done

Each script prints one PASS or FAIL line per check and names, in its header, the release note sentence it backs. The index with each script’s check count is bench/release_checks/README.md.