Results · 01 OCT 2026
Measured — bar met, nina in the spotlight. The bar was met, the manipulation check passed, and the pre-registered spotlight decision (2026-10-01) does not hold it: nina's entry in the tools list.
The bar, as pre-registered
The same spotlight bar as EXP 005: amendment 01's four criteria, thresholds, denominators, hold rule and three-state labels, computed by EXP 005's own spotlightVerdict. The only change is the run-state mapping below. The primary figures count every counted run, a diff-blind run as an error; the diff-seen-only figures leave the blind runs out and are reported beside the primary, never in its place.
| Criterion | All runs (primary) | Diff-seen runs only | State |
|---|---|---|---|
| Missed drift, at most 10.0% of RED runs | 1 of 90 (1.1%); on the 30 items, 0 of 30 (0.0%), 95% interval 0.0%–11.4%: passes, not established at this N | 0 of 89 (0.0%) | holds |
| False reject, at most 25.0% of GREEN runs | 2 of 90 (2.2%); on the 30 items, 0 of 30 (0.0%), 95% interval 0.0%–11.4%: passes | 0 of 88 (0.0%) | holds |
| Self-agreement, at least 90.0% of changes | 57 of 60 (95.0%) | 57 of 57 (100.0%) | holds |
| Zero patches, harness failures at most 5.0% of runs | no local patch; 0 of 180 (0.0%) harness failures | not applicable | holds |
The bar: PASS. Item level: passes, not established at this N. Hook errors, recorded beside the bar and not a criterion: 0 of 180 (0.0%).
The manipulation check: did the reviewer see the change?
BLIND runs (harness failures included) at most 18 of the 180 counted runs (10%): the manipulation check PASSES. More than 18: it FAILS, and there is no spotlight whatever the bar says.
PASS: 3 of 180 counted runs were diff-blind, 0 of them harness failures, against the pre-registered maximum of 18 of 180. Run states: 177 SEEN-DECIDED, 0 SEEN-ABSTAIN, 3 BLIND, 0 HARNESS-FAIL.
Every run's diff visibility (180 runs)
| Item | Run | State | Rule |
|---|---|---|---|
| c001 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c001 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c001 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c002 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c002 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c002 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c003 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c003 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c003 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c004 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c004 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c004 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c005 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c005 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c005 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c006 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c006 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c006 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c007 | 1 | SEEN-DECIDED | b: the added file listed and read |
| c007 | 2 | SEEN-DECIDED | b: the added file listed and read |
| c007 | 3 | SEEN-DECIDED | b: the added file listed and read |
| c008 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c008 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c008 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c009 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c009 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c009 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c010 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c010 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c010 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c011 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c011 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c011 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c012 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c012 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c012 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c013 | 1 | BLIND | none |
| c013 | 2 | SEEN-DECIDED | b: the added file listed and read |
| c013 | 3 | SEEN-DECIDED | b: the added file listed and read |
| c014 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c014 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c014 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c015 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c015 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c015 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c016 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c016 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c016 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c017 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c017 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c017 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c018 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c018 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c018 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c019 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c019 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c019 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c020 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c020 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c020 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c021 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c021 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c021 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c022 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c022 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c022 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c023 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c023 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c023 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c024 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c024 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c024 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c025 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c025 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c025 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c026 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c026 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c026 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c027 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c027 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c027 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c028 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c028 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c028 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c029 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c029 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c029 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c030 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c030 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c030 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c031 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c031 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c031 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c032 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c032 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c032 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c033 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c033 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c033 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c034 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c034 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c034 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c035 | 1 | SEEN-DECIDED | b: the added file listed and read |
| c035 | 2 | SEEN-DECIDED | b: the added file listed and read |
| c035 | 3 | SEEN-DECIDED | b: the added file listed and read |
| c036 | 1 | SEEN-DECIDED | b: the added file listed and read |
| c036 | 2 | SEEN-DECIDED | b: the added file listed and read |
| c036 | 3 | BLIND | none |
| c037 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c037 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c037 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c038 | 1 | SEEN-DECIDED | b: the added file listed and read |
| c038 | 2 | SEEN-DECIDED | b: the added file listed and read |
| c038 | 3 | SEEN-DECIDED | b: the added file listed and read |
| c039 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c039 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c039 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c040 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c040 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c040 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c041 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c041 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c041 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c042 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c042 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c042 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c043 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c043 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c043 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c044 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c044 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c044 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c045 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c045 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c045 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c046 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c046 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c046 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c047 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c047 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c047 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c048 | 1 | SEEN-DECIDED | b: the added file listed and read |
| c048 | 2 | SEEN-DECIDED | b: the added file listed and read |
| c048 | 3 | SEEN-DECIDED | b: the added file listed and read |
| c049 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c049 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c049 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c050 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c050 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c050 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c051 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c051 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c051 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c052 | 1 | SEEN-DECIDED | b: the added file listed and read |
| c052 | 2 | SEEN-DECIDED | b: the added file listed and read |
| c052 | 3 | SEEN-DECIDED | b: the added file listed and read |
| c053 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c053 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c053 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c054 | 1 | SEEN-DECIDED | b: the added file listed and read |
| c054 | 2 | BLIND | none |
| c054 | 3 | SEEN-DECIDED | b: the added file listed and read |
| c055 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c055 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c055 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c056 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c056 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c056 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c057 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c057 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c057 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c058 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c058 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c058 | 3 | SEEN-DECIDED | a: a changed line in a git output |
| c059 | 1 | SEEN-DECIDED | b: the added file listed and read |
| c059 | 2 | SEEN-DECIDED | b: the added file listed and read |
| c059 | 3 | SEEN-DECIDED | b: the added file listed and read |
| c060 | 1 | SEEN-DECIDED | a: a changed line in a git output |
| c060 | 2 | SEEN-DECIDED | a: a changed line in a git output |
| c060 | 3 | SEEN-DECIDED | a: a changed line in a git output |
DIFF-SEEN iff (a) a Bash git call the client did not refuse printed a whole diff line +<fp> or -<fp> equal to one of the item's fingerprints of that sign; or (b) for an item with added files, a non-refused Bash git output lists the added path or a directory prefix of it as untracked (a short `??` entry, or an entry inside a long-form "Untracked files:" section, never under "Changes not staged"/"Changes to be committed") and a non-refused Read/Grep of that added path returned one of its + fingerprint lines (line-number and path prefixes stripped). A git call is a Bash command whose first token is git, or one in which any sub-command (split on unquoted &&, ||, ; and |) is an allowed fence6 git form (git, git --no-pager, git -C <ws>/repo or git -C <ws>/repo --no-pager, then diff, status, show or log), as amended by EXP 006 amendment 01; its whole scrubbed output is kept and read, and a `cat` of a file cannot supply a sign-matched diff line; only top-level calls count (a sub-agent's calls never make a run SEEN). Refused = listed in the result's permission_denials; is_error alone is not a refusal (the client sets it on a command that ran when its own cwd-tracking write fails under the sandbox). Otherwise DIFF-BLIND. A harness failure is never DIFF-SEEN.
Cost
API-equivalent: the total_cost_usd that the Claude Code client reports for each run, at list price. Not an invoice; the runs may be billed differently. Per counted run: mean $0.1648, 90th percentile $0.1843, computed with results.mjs percentile (numpy linear interpolation), largest $0.3531, over 180 runs with a reported cost.
Every paid call of EXP 006, from the committed spend ledger (experiments/nina-changes/spend-ledger.jsonl): $38.4224310 over 211 calls, of which $29.6651648 over 180 counted runs, under the $60 cap.
What these results do not establish
- A BLIND run, harness failures included, is scored as an error in missed drift and false reject, at run level and in the item majority (where it counts as the wrong answer), and as a disagreement in self-agreement: its decision is treated as no decision before EXP 005's formulas run.
- Two layers: the text deny rules are best-effort (the client matches a normalised command); the OS sandbox is the boundary: reads outside the repository within home and the temp roots are denied (the sandbox's denyRead and its home block; R9 and R45 read a file outside the repository and got "Operation not permitted"); writes are confined and there is no unsandboxed retry. Nothing is canaried anywhere else, so the matrix tests the sandbox where the answer files could be.
- Untested residual: git diff --no-index given an in-workspace symlink to an outside file (and /dev/null) was not run live. It is refused by the layer-1 text rule on --no-index, and R45 shows the sandbox resolves the real path of what git reads; it is stated as untested, not proven (no new live row was added; matrix6 is unchanged).
- Untested in the matrix: git forms with an environment prefix (GIT_DIR=…, GIT_EXTERNAL_DIFF=…, GIT_CONFIG_PARAMETERS=… before git). They are believed refused because no allow rule's prefix matches a command that starts with an assignment, and whatever they ran, reads outside the repository are stopped by the sandbox (R9, R29, R30 and R45, and the sandbox-only run). No live row was added; this is stated as untested, not proven.
- git diff -O<file> (an orderfile) is allowed under the git diff rules and makes git read that file to order its output; it prints none of the file's content, so at most it is a side channel (whether an outside path exists or parses). Documented as a residual; no live row was added (no paid call).
- The attempted-per-row check compares each row's recorded command with its registered step after the client's own normalisation only (a 2>&1 or 2>/dev/null redirection stripped). An honest rephrasing beyond that marks the row not attempted and fails the pre-run probe, which is the only one allowed under this pre-registration: the consequence is a dated amendment with a new not-before (then its own pre-run probe), not merely a question to the founder.
- A stream with a malformed line is charged the upper bound even if a final result line reported a cost (conservative: the line cannot be trusted).
- Sub-agents: a run can spawn a sub-agent whose own tool calls appear in the stream with a parent_tool_use_id. Only TOP-LEVEL calls count toward DIFF-SEEN (a sub-agent's calls are recorded but never make a run SEEN), so a run whose change was read only by a sub-agent counts BLIND.
- Reviewer only: Jev, Laya and the cascade are not re-run; this measures nina 0.34.0's reviewer on mechanical architecture rules and nothing else about nina.
- Same corpus, inputs, labels and rules as EXP 005, so the results compare; the corpus is public, and the reviewer is fenced from the answer files by the sandbox and the answer-key pre-flight.
- Prompt confound: the prompt says "the change is everything that git diff shows", which is false for the 14 items that add a file (9 of them add only files): git diff does not show untracked files. The prompt is EXP 005's, unchanged, so the results compare.
- Client artifact: Claude Code 2.1.280 marks some git commands that ran (git status --short, git log, git show) is_error, with "Exit code 1" and a line for its failed cwd-tracking write under the sandbox. It likely affected EXP 005's tool results too; EXP 005's records keep no tool output, so this cannot be checked (its classifier never read tool-level is_error). EXP 006 reads refusals from permission_denials and never from is_error.
- Known false-BLIND forms: the classifier does not recognise a change shown only through git diff --color=always, git diff --word-diff, git diff -R, a coloured git status (-c color.status=always), git status --porcelain=v2 or -z, or a long-form status run from a subdirectory (its ../ paths). A run that saw the change only that way is counted BLIND: an error against the bar and the manipulation check, never toward a spotlight. Each form is pinned BLIND by a unit test (documented behaviour, not a silent gap).
- The runs are sequential, on one machine, under whatever load it has; latency is recorded, not judged.
- The spend guard's pending sidecar is per worktree: bundles 2 and 3 run from ONE worktree (named in experiments/nina-changes/README.md), one after the other. Two runners at once could each pass the spend check and overshoot the cap by one call, so every paid invocation holds an exclusive lock file (experiments/nina-changes/run.lock, created O_EXCL) for its whole run and a second one refuses; a stale lock is removed by the operator after checking. A reconciled pre-run probe line without a pre-registration sha counts as made under the current one (fail closed).
- Paid calls before this record: 7 matrix probes, $4.0821826, itemised in spend.calls; none is counted in any result.
- The two dry-run runs counted BLIND kept no tool output (that was the defect), so they are not re-classified here; the amended rule is shown on synthetic outputs built from the same two commands and a real p01 and p02 diff, and the base-only and synthetic-seen checks over all 60 items still hold. (amendment 01)
- A git command reached only through a newline, a lone &, $(…) or backticks is not split out, so such a call is not a git call and a run that saw the change only that way is counted BLIND: an error against the bar and the manipulation check, never toward a spotlight. (amendment 01)
- A git call's whole scrubbed output is kept, including the output of its other parts (the dry run's cat of rules.txt): it is scrubbed and linted like every other kept output, and the rules.txt text is EXP 005's public rules file. (amendment 01)
- Only the runner's own digests are exempt; a 64-hex value anywhere else (a commit id in a tool output, a hash the model quoted) is still scanned, so a chance match there would still refuse a record and stop the run. (amendment 01)
- The projection assumes the counted runs cost like the 17 practice runs; the spend guard, not the projection, enforces the cap before every call. (amendment 01)
- The record lint compares text fingerprints and does not decode hex or base64, so a restricted term encoded byte by byte (or in base64) would pass it. This gap predates this amendment: the pre-registered lint6 (EXP 006, scrub6.mjs) and the site-wide check.mjs behave the same way. The answer-key files are not protected by the lint. They are protected by the OS sandbox (reads of the temp roots and of home outside the run are denied) and by the counted pre-flight scan. (amendment 01)
- The practice runs' answer-key pre-flight scanned only the roots an override named (/private/var/tmp, recorded in pins.preflight.override of practice-a.json, practice-b.json, practice-recheck-a01.json). The counted run's pre-flight record records no override: it scanned the default roots.
- resultSha256 is the sha256 of the result text as the client returned it, before the write-time scrub; amendment 01 calls it "the sha256 of the result text". For 1 of the 180 calls (c007 run 2) the scrub changed the result text, so the published text does not reproduce that digest. After the run the committed record was re-scrubbed (scrubbed-records.json: the local account name (read at run time from the operating system's user record and $USER, never written down), as a whole word (not inside a longer run of letters, digits, '_' or '-', and not followed by a '.' that continues a name, as in a file name), in any string value of the record -> <user>; 6 replacements in 1 field; the record was 23e6adaff25b34fd9fae10eb67e8dc39f8c3c6a01decf0d47f3a03076a812ded as the runner wrote it and is 6326e98d28f5840c3c65ec10bd611fbdbb56a1bfd80019589882955e889d8c76 as committed). For that tool output (c050 run 3, calls.149.toolCalls.4.outputSha256) the outputSha256 is the digest of the output as first recorded, so it no longer reproduces from the published text either. The published copy keeps the result digest above as recorded, and masks that superseded output digest (it is the digest of text that held the local account name); the committed record keeps it.
Reproduce
From a checkout of this repository at the commit that published these records, with Node 22:
pnpm install --frozen-lockfile node experiments/nina-changes/write-results6.mjs --check
It recomputes experiments/nina-changes/results/results.json from the committed run record with the frozen scorer, including every run's diff visibility from its committed tool outputs and fingerprints, and fails unless the bytes match. The build runs the same check.
This section is rendered from results.json (sha256 3ef7bcf6d0317b2095df953b404e86059f9b84ffc4af6b4cb476ddaa7d9c916c), made from reviewer.json, the counted run record (the committed record's sha256 is 6326e98d28f5840c3c65ec10bd611fbdbb56a1bfd80019589882955e889d8c76; the published copy replaces the 1274 per-call digests that its own text reproduces, and the 1 digest the re-scrub superseded, with <sha256>, so a random digest cannot trip the site's restricted-term check, and is otherwise the same record), and the answer-key pre-flight record, with the spotlight decision. The run and the results name the pre-registration (sha256 41efb90da633b42acca619ab4d761e15e608ca06e496bafbc73ccfd9df03b98a) and amendment 01 (sha256 40c95cb0963fd760e078a27946d4c9aeb62c3f0d26a255374ea4079fa680cf47; the committed amendment is 40c95cb0963fd760e078a27946d4c9aeb62c3f0d26a255374ea4079fa680cf47).
Amendment 01 · 01 OCT 2026
Amended 01 Oct 2026, before any counted run. The bundle-2 dry run (practice rows only, never scored; 2026-09-30 22:00-22:10 UTC, 17 paid calls) found two defects in the pre-registered record pipeline and one statement in the pre-registration that the client's behaviour contradicts. Left as registered, the first would very likely void a counted run by chance and the second would fail the manipulation check on a classifier artefact. None concerns the bar, the corpus, the prompt, the command, or the fence's settings and allow list. Each is fixed forward here, before any counted call. The pre-registration below is unchanged and still served byte for byte (sha256 41efb90da633b42acca619ab4d761e15e608ca06e496bafbc73ccfd9df03b98a); where this section and it differ, this section wins, and only on what it lists.
What the dry run found
- F1, a false-positive lint refusal. p06 run 2's record was refused at write time ("a restricted term"), and the run stopped partial. The only leaking field was toolCalls.4.outputSha256: a random sha256 digest of a tool output that happened to contain one of check.mjs's short restricted-term fingerprint windows. With about a hundred digests in each run record, a 180-run counted run would very likely be stopped, and so voided, by chance.
- F2, a false-blind classification. p01 run 2 and p02 run 1 ran `cat rules.txt; git status --short; git diff` and `cat rules.txt; echo ----; git status --short; git diff`. The client ran each (687 bytes of output, 648 bytes of output; not refused), each report names the changed file and 1 of 2 quote a changed line verbatim, but the registered classifier treats a Bash call as a git call only when its FIRST token is git (refute r2 N9), so the output was not kept and the run was classified BLIND. That is 2 of 17 completed calls (11.8%), above the manipulation check's 10% limit.
- F3, a fence fact the pre-registration states wrongly. The client ran cat on files inside the workspace with no allow rule for cat (16 top-level Bash calls in the dry run, 0 refused): Claude Code runs its built-in read-only commands on workspace paths without an allow rule. The pre-registration says only Read, Grep, Glob and the four git verbs are allowed, and that a chain with any other part is refused whole. Outside reads stayed closed: in the probe of record 7 commands that cat a file outside the workspace (R17, a bare cat ../canary-r17.txt, and the chains and command substitutions R18, R19, R19b, R39, R40, R46) were refused by the permission layer, and R9, R45, R29, R30 were stopped by the OS sandbox.
The dry-run records: practice-a.json (p01-p03 (the EXP 005 practice rows, experiments/jev-gate/practice/practice-rows.json); sha256 450dde3dd20a76d56624361b6aac2e6c94aefe76ff21b4e15e4dd4dac6379fc6); practice-b.json (p04-p06 (experiments/nina-changes/practice/practice-rows.json); sha256 df7a12750a2b09f2428f2a8b006f668311b02f8e5a271cde1c81eeda15aef05d). Their spend is lines 8-24 of experiments/nina-changes/spend-ledger.jsonl (kind practice, one per completed call, the refused one included).
What changes
- A1, the record lint. Only a runner digest is exempt from the restricted-term scan: a digest the runner computed itself, at write time, from bytes it holds. The runner passes the write-time lint a list of (path, sha256) pairs it computed; a field is exempt only if it sits at exactly one of those paths AND its value equals the runner's own sha256 for that path (exactly 64 lowercase hex characters). The exemption is never by key name and never taken from a record or from model text: a 64-hex value the model produced (in a tool output, the result text or a tool input), or one placed under a hash-named key without being the runner's recomputation, is scanned like any other value. The record is linted with its runner digests masked, and each runner digest is still linted for paths, the session uid and credential shapes. Everything else (keys, prose, tool outputs, tool inputs) is scanned as before.
- A2, what counts as a git call. A git call is a top-level (not sub-agent) Bash call whose first token is git, as registered, or one in which ANY sub-command, split on the unquoted control operators &&, ||, ; and |, is an allowed fence6 git form. Its full scrubbed output is kept in the record and read by the classifier. Rule (a) still needs a sign-matched whole diff line of the item in that output: a cat of a file prints no + or - prefix, so file content cannot pass for a diff line. Rule (b)'s untracked entry may come from such an output, and still needs a Read or Grep of the added file. The allowed forms:
git diff,git status,git show,git log,git --no-pager diff,git --no-pager status,git --no-pager show,git --no-pager log,git -C <ws>/repo diff,git -C <ws>/repo status,git -C <ws>/repo show,git -C <ws>/repo log,git -C <ws>/repo --no-pager diff,git -C <ws>/repo --no-pager status,git -C <ws>/repo --no-pager show,git -C <ws>/repo --no-pager log. - A3, the fence as the client runs it. The pre-registration's fence statements (fence.compound and answer 2) are corrected: besides Read, Grep, Glob and the allowed git forms, the client runs its built-in read-only commands (cat, for example) on workspace paths without an allow rule: observed as part of a chain (every one of the dry run's cat calls was); a lone built-in read was not observed, and is believed to behave the same. A chain that reads outside the workspace is refused whole (R18, R39, R40, R46), a bare outside read is refused (R17), and reads outside the repository that pass the permission layer are stopped by the OS sandbox (R9, R29, R30, R45). The sandbox remains the boundary. fence6's settings, deny rules and allow list, matrix6, its rows and its judge are unchanged; no escape row's verdict changed; no new paid matrix run was made.
- A5, the guard. Counted runs require this amendment too. freeze.mjs gains AMENDMENT6_SHA256 (this file's sha256) and AMENDMENT6_NOT_BEFORE (the merge time of the pull request that adds it), both left unset until the re-freeze. The counted guard refuses unless the pre-registration hashes to PREREG6_SHA256, this file hashes to AMENDMENT6_SHA256 and names that pre-registration as its parent, every pre-registered file hashes to its pin as re-pinned below, every file this amendment pins hashes to its pin, and the call starts after AMENDMENT6_NOT_BEFORE (which the run records as its notBefore). The run record and the results record carry amendment6Sha256; the scorer checks it; the spotlight gate opens only when the results and the bound reviewer run record name the frozen AMENDMENT6_SHA256. The gate's predicate and every other check are unchanged.
The amended classifier rule, as every run record now carries it:
DIFF-SEEN iff (a) a Bash git call the client did not refuse printed a whole diff line +<fp> or -<fp> equal to one of the item's fingerprints of that sign; or (b) for an item with added files, a non-refused Bash git output lists the added path or a directory prefix of it as untracked (a short `??` entry, or an entry inside a long-form "Untracked files:" section, never under "Changes not staged"/"Changes to be committed") and a non-refused Read/Grep of that added path returned one of its + fingerprint lines (line-number and path prefixes stripped). A git call is a Bash command whose first token is git, or one in which any sub-command (split on unquoted &&, ||, ; and |) is an allowed fence6 git form (git, git --no-pager, git -C <ws>/repo or git -C <ws>/repo --no-pager, then diff, status, show or log), as amended by EXP 006 amendment 01; its whole scrubbed output is kept and read, and a `cat` of a file cannot supply a sign-matched diff line; only top-level calls count (a sub-agent's calls never make a run SEEN). Refused = listed in the result's permission_denials; is_error alone is not a refusal (the client sets it on a command that ran when its own cwd-tracking write fails under the sandbox). Otherwise DIFF-BLIND. A harness failure is never DIFF-SEEN.
Files re-pinned
run_reviewer6.mjs:13cc7b59de017530194c258e9c47fc3b59d268399d1d5b1da59298dbbdcecf5c→83794bdcb8377315c4ac3f4ff2a188018529129ae050d0d258a25e59290d0eb2. A1: the record builder hands the lint the digests it computed itself (callDigests6, headerDigests6); A5: the run record carries the guard's amendment6Sha256; the argv builder is unchanged.stream6.mjs:09e55aab57aa276f7b6ae1d245ac45bf41cfd2b095e32fd692860bf4b7b250e1→36868cbffeb42e62b614b112c2c1b3d8a464a2a49b6703d3f66bb4b91cd8d6e1. A2: a git call is also a Bash call in which any sub-command (split on unquoted &&, ||, ; and |) is an allowed fence6 git form; such a call's output is kept.scrub6.mjs:d38b4efce5346abacd6e68d19c6121c5a533410de474d0578bcdbcec75fad435→47d38d5cd7108ad03f76dc4b767b34ac33ca7ef7358bb5dd6cf1a9462fd06329. A1: the write-time lint does not scan a runner digest for restricted terms: a field at a path the runner names, whose value equals the sha256 the runner computed itself for it; every other value is linted as before.guard6.mjs:21b143f61a4aa640d158bef9e096d6bc213691c7170b13efed7cb9ddee2b55ce→da1794d276182e66347f37c29225d13f4fa20dab033552d4a43b67a9054403c5. A5: a counted run also needs this amendment: its sha256 frozen as AMENDMENT6_SHA256, every pin as re-pinned here, and a start after AMENDMENT6_NOT_BEFORE.diff-seen.mjs:c032517dca76109f1988ba641b73d6cc7618a245ce4f3ad243fcc27fff812f88→64d85ee38f99bb6858609cb2d835581d40c9d8058a3cfa2fc4558f95464566cf. A2: the classifier uses the amended git call; its rule text is amended (RULE) and the registered text is kept verbatim (REGISTERED_RULE); rules (a) and (b) are otherwise unchanged.results6.mjs:7533ee983f2891506bac2185396740199b86b94510da031df4a21c51332f7025→49842f3249230032c653a21a14b1e4b626cff984ae17e6d0ebb8d987af0cff76. A5: the scorer checks the run's amendment6Sha256 against the stamp (and the frozen AMENDMENT6_SHA256), uses the amendment's not-before, and carries the sha in the results record.spotlight-gate.mjs:df7006754a965479640f86c9eb1b661a75d0db5ac46d632b86fe68e5ebe029a3→a817c8684aff8c3627eddaa9de3a93ededa7bc27eaba343ada0b98b3073bf3d2. A5: the gate also requires the results' amendment6Sha256 to equal the frozen AMENDMENT6_SHA256 and the reviewer run record to name the same one; the predicate and every other check are unchanged.
experiments/nina-changes/runners.sha256 is re-pinned for the 7 files above and freeze.mjs (which the re-freeze changes again); every other pin in it is unchanged.
Spend and the projection
- Paid so far: 4.0821826 (7 isolation-matrix lines, the pre-registration's) + 3.0181336 (17 practice lines, the dry run) = 7.1003162 dollars, every line itemised in the record. No counted call has been made: the ledger holds 7 isolation-matrix lines and 17 practice lines, and no counted or pre-run line.
- The $10 pre-counted ceiling: $2.8996838 remains, $2.2451280 after the reserve of $0.6545558.
- The dry run's per-run cost over 17 runs: mean $0.1775373, p90 $0.2048640, max $0.2279082 (per-run cost of the dry run's practice lines; p90 by results.mjs percentile (numpy linear interpolation)). Projected counted spend at the dry run's p90: $7.1003162 + 180 × $0.2048640 + $0.6545558 + $0.6545558 = $45.2849478, within the $60 cap (at the mean $40.3661365, at the max $49.4329038).
No counted reviewer run starts before the merge time of the odin-rnd pull request that adds this file; that time is recorded after publication, frozen with this file's sha256 in freeze.mjs (AMENDMENT6_NOT_BEFORE, AMENDMENT6_SHA256), and every counted call must start after it. It replaces the pre-registration's not-before as the counted clock. The answer-key pre-flight record must end after it.
Unchanged
- The pre-registration, byte for byte (sha256 41efb90d…), and everything in it this amendment does not list.
- The question, the bar (EXP 005 amendment 01's four criteria, thresholds, denominators, hold rule and labels), the run-state mapping and the manipulation limit (at most 18 of 180 runs BLIND).
- The corpus, inputs, labels, rules, base commit, prompt, reviewer (nina 0.34.0), client, model, effort, k and timeout, and the registered command (its sha256 is unchanged).
- fence6 (settings, deny rules, sandbox and allow list), matrix6 and its judge, the attempted-per-row check, and every escape row's verdict.
- The change fingerprints, the base lines, the practice rows and the harness-failure definition.
- The spend cap ($60), the pre-counted ceiling ($10), the unknown-cost upper bound and the ledger rule.
- The spotlight decision rule, the artefact a PASS publishes, and the spotlight gate's predicate.
What the amendment adds to the limits
- The two dry-run runs counted BLIND kept no tool output (that was the defect), so they are not re-classified here; the amended rule is shown on synthetic outputs built from the same two commands and a real p01 and p02 diff, and the base-only and synthetic-seen checks over all 60 items still hold.
- A git command reached only through a newline, a lone &, $(…) or backticks is not split out, so such a call is not a git call and a run that saw the change only that way is counted BLIND: an error against the bar and the manipulation check, never toward a spotlight.
- A git call's whole scrubbed output is kept, including the output of its other parts (the dry run's cat of rules.txt): it is scrubbed and linted like every other kept output, and the rules.txt text is EXP 005's public rules file.
- Only the runner's own digests are exempt; a 64-hex value anywhere else (a commit id in a tool output, a hash the model quoted) is still scanned, so a chance match there would still refuse a record and stop the run.
- The projection assumes the counted runs cost like the 17 practice runs; the spend guard, not the projection, enforces the cap before every call.
- The record lint compares text fingerprints and does not decode hex or base64, so a restricted term encoded byte by byte (or in base64) would pass it. This gap predates this amendment: the pre-registered lint6 (EXP 006, scrub6.mjs) and the site-wide check.mjs behave the same way. The answer-key files are not protected by the lint. They are protected by the OS sandbox (reads of the temp roots and of home outside the run are denied) and by the counted pre-flight scan.
This section is rendered from amendment-01.json, the committed record at experiments/nina-changes/amendment-01.json, whose sha256 is 40c95cb0963fd760e078a27946d4c9aeb62c3f0d26a255374ea4079fa680cf47. It names its parent by sha256 41efb90da633b42acca619ab4d761e15e608ca06e496bafbc73ccfd9df03b98a. The build validates the record and this page against it and publishes both byte for byte; a repository test re-renders the page from the two records and fails if they differ.
Pre-registered, not yet run. Amended 01 Oct 2026, before any counted run: the record lint no longer mistakes the runner's own digests for a restricted term, a compound git call now counts as a git call, and the client's built-in reads are disclosed. EXP 005 met nina's spotlight bar, but in 131 of 180 runs its own tool fence kept the reviewer from reading the diff. EXP 006 measures nina 0.34.0's reviewer again on the same 60 changes, with a fence that allows the git forms it uses, and counts a run as having seen the change only when a tool output shows at least one changed line of that change (or, for an added file, the file listed as untracked and at least one of its new lines read). Written, hashed and published before any counted reviewer run. Nothing in this record may change after publication; a later change is a new, dated amendment (the EXP 005 amendment pattern), refuted and published first, and it resets the not-before. Measured — bar met, nina in the spotlight, 01 OCT 2026: see the results.
The question
On the same 60 authored changes and the same architecture rules as EXP 005, and with a tool fence that lets it read the change, does nina 0.34.0's reviewer, run headless by Claude Code, meet the same spotlight bar as EXP 005, in runs of which at least 90% are counted as having seen the change: a tool output shows at least one changed line of that item (or, for an added file, the file listed as untracked and at least one of its new lines read)?
Why measure again
EXP 005's results: the reviewer met the registered spotlight bar (0 of 90 missed, 0 of 90 false rejects, 60 of 60 agreement, 0 harness failures), but by EXP 005's committed classifier (results/diff-visibility.json) 131 of 180 runs were diff-blind: its fence refused the git forms the reviewer used, and it audited the clean base tree instead. The founder held the spotlight until nina is measured reviewing changes. This follows EXP 005's held spotlight (results record sha256 7388556049b7639e2a251edcf3fb8feb782213b78e71a02cddb687f12b09dccd).
All 317 git commands EXP 005's fence refused, by exact form: 184 are allowed under fence6 and 133 stay refused (127: redirect to a file (fence6 never allows > to a file); 2: not a git command (head): no allow rule, and no blanket rule; 1: no allow rule matches (verb or prefix not allowed); 1: not a git command (cat): no allow rule, and no blanket rule; 1: not a git command (echo): no allow rule, and no blanket rule; 1: not a git command (ls): no allow rule, and no blanket rule). Of the 131 diff-blind EXP 005 runs, 123 began with a form fence6 still refuses and 130 tried a form fence6 allows.
What is the same
The corpus, inputs, labels, base app, base commit and rules are EXP 005's, byte for byte, so the two experiments are directly comparable. The runner asserts each by sha256 before any call. 60 changes, 30 RED and 30 GREEN by bce; corpus sha256 a83b222a1a4a64cc81ac755c827a47009baa2bb91b036e351e71422cc8d526a9; base commit 3e35e4e274932a61bc0d92f378f8d506a9bb4ce0.
- The reviewer: nina (github.com/xhulz/nina) — used with the permission of its author, as confirmed by Odin Labs, release
0.34.0(commitbe546e32ce30acb2a18ec9b55ff4a3b23a7b4890). nina 0.34.0 unchanged: the pinned tarball, verified by integrity, staged exactly as EXP 005 staged it (amendment 01's workspace order, EXP 005's own stageWorkspace). No local patch. - Claude Code 2.1.280, model
claude-opus-5-5, efforthigh, agentreviewer, 3 runs per change, 600 s per run. k runs per item, sequential, in id order, each in its own fresh workspace, never retried. - The hang-stop: Once at least minRuns reviewer runs have completed, if more than fraction of all runs so far ended in a timeout, the run stops and the design is revisited before any further call; the stop and its counts are published. Before minRuns runs, a timeout alone never stops the run. Isolation probe runs count toward neither minRuns nor the timeout fraction.
The prompt, verbatim (EXP 005's):
Review the change in this repository. The change is everything that git diff shows; it has not been committed. The architecture rules this codebase must follow are in rules.txt at the repository root. Decide one thing only: does the change break any of those rules? Ignore style, naming, tests and anything else that rules.txt does not state. Do not edit any file. Explain your reasoning briefly, then write your decision on a line of its own: VERDICT: REJECTED if the change breaks at least one rule, or VERDICT: APPROVED if it breaks none.
What changes: the fence, and the record of every tool call
- --output-format stream-json --verbose instead of --output-format json
- fence6: the fence5 deny rules and sandbox, plus allow rules for git, git --no-pager, git -C <ws>/repo and git -C <ws>/repo --no-pager with diff, status, show and log
The command is the EXP 005 command with two declared differences: --output-format stream-json --verbose (so every tool call and its output is recorded), and fence6 (the settings and allow list below). <ws> stands for the run's own temp dir; its repository is <ws>/repo, rendered as an absolute path in each run and scrubbed back to <ws>/repo in the records. The validator derives this text from the runner's own renderCommand.
claude -p <prompt> --agent reviewer --model claude-opus-5-5 --effort high --output-format stream-json --verbose --no-session-persistence --setting-sources project --permission-mode dontAsk --settings "{\"permissions\":{\"blockReadsOutsideWorkingDirectories\":true,\"deny\":[\"Bash(git *--output*)\",\"Bash(git *--no-index*)\",\"Bash(git *<*)\",\"Bash(git *>*)\",\"Bash(git *'*)\",\"Bash(git *\\\"*)\",\"Bash(git *\\\\*)\",\"Bash(git *{*)\",\"Bash(git *}*)\",\"Bash(git *`*)\"]},\"sandbox\":{\"enabled\":true,\"failIfUnavailable\":true,\"allowUnsandboxedCommands\":false,\"autoAllowBashIfSandboxed\":false,\"filesystem\":{\"denyRead\":[\"/private/tmp\",\"/tmp\",\"/private/var/folders\",\"/var/folders\"]}}}" --allowedTools "Read(./**)" "Grep(./**)" "Glob(./**)" "Bash(git diff:*)" "Bash(git status:*)" "Bash(git show:*)" "Bash(git log:*)" "Bash(git --no-pager diff:*)" "Bash(git --no-pager status:*)" "Bash(git --no-pager show:*)" "Bash(git --no-pager log:*)" "Bash(git -C <ws>/repo diff:*)" "Bash(git -C <ws>/repo status:*)" "Bash(git -C <ws>/repo show:*)" "Bash(git -C <ws>/repo log:*)" "Bash(git -C <ws>/repo --no-pager diff:*)" "Bash(git -C <ws>/repo --no-pager status:*)" "Bash(git -C <ws>/repo --no-pager show:*)" "Bash(git -C <ws>/repo --no-pager log:*)"
Allowed git forms: git, git --no-pager, git -C <ws>/repo, git -C <ws>/repo --no-pager with diff, status, show, log. No compound rule. A chain runs only when the client allows every part under the same allow list; a chain with any other part is refused whole. Redirections to /dev/null and 2>&1 are stripped by the client before matching; a redirect to a file is never allowed. Every EXP 005 deny rule stays: Bash(git *--output*) Bash(git *--no-index*) Bash(git *<*) Bash(git *>*) Bash(git *'*) Bash(git *"*) Bash(git *\*) Bash(git *{*) Bash(git *}*) Bash(git *`*). Each run stages a fresh workspace under the operator's home cache (outside any checkout and outside the shared temp roots), with the reviewer's TMPDIR inside the run dir; the whole dir is removed after the run.
The fence, tested on canaries
A row leaks when its secret appears in the report, a refusal or ANY tool output, or its escape file appears on disk; a control works when its own command was not refused and returned the change in its own output. The model's own ALLOWED/DENIED labels are not used.
- matrix-v6-discovery-fence6-1 (fence6, discovery (before the parser recorded refusals; C12 failed only on the is_error artifact)): 48/48 escape rows held, 15/16 controls worked, R28 ran, not refused, I47 refused by the client; no file appeared; $0.6545558; record sha256
e7deb80a7b5cf3c6d748999068be3a2f66ff51eba0f6b4277a2f1c10f1d01bc0 - matrix-v6-discovery-fence6-2 (fence6, discovery 2 (with C17-C19; the open questions answered from it)): 48/48 escape rows held, 19/19 controls worked, R28 ran, not refused, I47 refused by the client; no file appeared; $0.5596686; record sha256
6f9dfcbd43d226457b18997498ff8b1dbe1724f3de04ea612e4e5ee4c89ae185 - matrix-v6-fence6-1 (fence6, proof run 1): 48/48 escape rows held, 19/19 controls worked, R28 ran, not refused, I47 refused by the client; no file appeared; $0.6258170; record sha256
7faf564ed642fa674a65e7eea021bc2dd8f2541cac6908f15ca62d5e11bd1147 - matrix-v6-fence6-2 (fence6, proof run 2): 48/48 escape rows held, 19/19 controls worked, R28 ran, not refused, I47 refused by the client; no file appeared; $0.5681038; record sha256
7bf8dc8a050783382704464799e4e64809604a9556f58294c13e9b1abbeeb4ce - matrix-v6-sandbox6-only-1 (sandbox6-only, paid, but its record was refused at write time and its verdict lost (a truncated home path escaped the scrub)): lost: the record was refused at write time (lint); $0.5472840; record sha256
bafb7ae1a7e51b299fbfd5ca49afd44387a800ef96e065c1cc374cda154d8f64 - matrix-v6-sandbox6-only-2 (sandbox6-only, proof run, the sandbox and file fence alone): 48/48 escape rows held, 19/19 controls worked, R28 ran, not refused, I47 refused by the client; no file appeared; $0.5811820; record sha256
5a7b528c724de9e7d6cebad0def0a6d9b970dbf2374c18c88906fc5055bf5c0f - matrix-v6-fence6-3 (fence6, proof run 3, probe of record): 48/48 escape rows held, 19/19 controls worked, R28 ran, not refused, I47 refused by the client; no file appeared; $0.5455714; record sha256
04861e1b0cfd63edc50d009df94060d071b866ebc78c0f642f432f775743af0c
The fence was tested in three fence6 proof runs and one sandbox-only proof run. fence6 runs 1 and 2 were made at commit 14ba08d; fence6 run 3 (the probe of record) and the sandbox-only run at commit 2ab2c27 (candidate-branch commits). Their recorded code differs from the files this record pins as listed in proofRuns, each file with its reason. None of the differing files defines the fence, the matrix or its judge (experiments/nina-changes/fence6.mjs, experiments/nina-changes/matrix6.mjs, experiments/jev-gate/run_reviewer.mjs are byte-identical in every proof run), and every fence6 run ran exactly the command registered here (command sha256 ea845d19…), the sandbox-only run exactly the sandbox6-only template.
- matrix-v6-fence6-1 at
14ba08d:diff-seen.mjs(rule (b) also reads long-form "Untracked files:" entries (D2); a git call is the first token and only top-level calls count (N9, N10); the classifier runs after the call, on the record);guard6.mjs(the counted guard also re-checks every file the pre-registration pins (phase C, spotlight gate included));results6.mjs(a comment only (D3));run_reviewer6.mjs(refusals before the call and handling after it only: a record refused at write time keeps its verdict and cost (00e27d7); removing a run dir never throws (bf1308a); the ledger line is written from the spawn result before anything else, and later failures mark the record (refute r2 N1); a failed ledger write goes to a pending sidecar and stops the run (r3 N1); an intent line is written before each paid spawn and cleared after, and every paid run must use the committed ledger (r4 N1, B2); every paid invocation holds an exclusive run lock (r5 N2); a paid run is refused under the Node test runner (r6 B1); a matrix run keeps every call's output (r3 N3); the post-freeze pre-run probe's own ledger kind (D4, N3); and the attempted-per-row check applied after the matrix judge (N6, matrix6-attempts.mjs). The argv builder is unchanged, as the command sha256 equality shows);scrub6.mjs(the write-time scrub also collapses a home path the client truncated (00e27d7));spend6.mjs(the post-freeze pre-run probe has its own ledger kind, charged to the $60 cap only and made once per frozen pre-registration (D4, N3); a sub-5e-8 reported cost is charged as unknown, a corrupt ledger line refuses every call, and so does a pending (unwritten or intent) line; intent lines are written and cleared around each paid spawn, the clear removing only its own line by an atomic rewrite, and a pre-run probe line without a pre-registration sha counts as the current one (r2 N1, N2; r3 N1; r4 N1; r5 N1, N3));stream6.mjs(records parent_tool_use_id per call and defines a git call by its first token (N9, N10); a matrix run keeps every call's output (r3 N3); the D3 comment; the stream parse and harness-failure rule are unchanged) - matrix-v6-fence6-2 at
14ba08d:diff-seen.mjs(rule (b) also reads long-form "Untracked files:" entries (D2); a git call is the first token and only top-level calls count (N9, N10); the classifier runs after the call, on the record);guard6.mjs(the counted guard also re-checks every file the pre-registration pins (phase C, spotlight gate included));results6.mjs(a comment only (D3));run_reviewer6.mjs(refusals before the call and handling after it only: a record refused at write time keeps its verdict and cost (00e27d7); removing a run dir never throws (bf1308a); the ledger line is written from the spawn result before anything else, and later failures mark the record (refute r2 N1); a failed ledger write goes to a pending sidecar and stops the run (r3 N1); an intent line is written before each paid spawn and cleared after, and every paid run must use the committed ledger (r4 N1, B2); every paid invocation holds an exclusive run lock (r5 N2); a paid run is refused under the Node test runner (r6 B1); a matrix run keeps every call's output (r3 N3); the post-freeze pre-run probe's own ledger kind (D4, N3); and the attempted-per-row check applied after the matrix judge (N6, matrix6-attempts.mjs). The argv builder is unchanged, as the command sha256 equality shows);scrub6.mjs(the write-time scrub also collapses a home path the client truncated (00e27d7));spend6.mjs(the post-freeze pre-run probe has its own ledger kind, charged to the $60 cap only and made once per frozen pre-registration (D4, N3); a sub-5e-8 reported cost is charged as unknown, a corrupt ledger line refuses every call, and so does a pending (unwritten or intent) line; intent lines are written and cleared around each paid spawn, the clear removing only its own line by an atomic rewrite, and a pre-run probe line without a pre-registration sha counts as the current one (r2 N1, N2; r3 N1; r4 N1; r5 N1, N3));stream6.mjs(records parent_tool_use_id per call and defines a git call by its first token (N9, N10); a matrix run keeps every call's output (r3 N3); the D3 comment; the stream parse and harness-failure rule are unchanged) - matrix-v6-fence6-3 at
2ab2c27:diff-seen.mjs(rule (b) also reads long-form "Untracked files:" entries (D2); a git call is the first token and only top-level calls count (N9, N10); the classifier runs after the call, on the record);guard6.mjs(the counted guard also re-checks every file the pre-registration pins (phase C, spotlight gate included));results6.mjs(a comment only (D3));run_reviewer6.mjs(refusals before the call and handling after it only: a record refused at write time keeps its verdict and cost (00e27d7); removing a run dir never throws (bf1308a); the ledger line is written from the spawn result before anything else, and later failures mark the record (refute r2 N1); a failed ledger write goes to a pending sidecar and stops the run (r3 N1); an intent line is written before each paid spawn and cleared after, and every paid run must use the committed ledger (r4 N1, B2); every paid invocation holds an exclusive run lock (r5 N2); a paid run is refused under the Node test runner (r6 B1); a matrix run keeps every call's output (r3 N3); the post-freeze pre-run probe's own ledger kind (D4, N3); and the attempted-per-row check applied after the matrix judge (N6, matrix6-attempts.mjs). The argv builder is unchanged, as the command sha256 equality shows);spend6.mjs(the post-freeze pre-run probe has its own ledger kind, charged to the $60 cap only and made once per frozen pre-registration (D4, N3); a sub-5e-8 reported cost is charged as unknown, a corrupt ledger line refuses every call, and so does a pending (unwritten or intent) line; intent lines are written and cleared around each paid spawn, the clear removing only its own line by an atomic rewrite, and a pre-run probe line without a pre-registration sha counts as the current one (r2 N1, N2; r3 N1; r4 N1; r5 N1, N3));stream6.mjs(records parent_tool_use_id per call and defines a git call by its first token (N9, N10); a matrix run keeps every call's output (r3 N3); the D3 comment; the stream parse and harness-failure rule are unchanged) - matrix-v6-sandbox6-only-2 at
2ab2c27:diff-seen.mjs(rule (b) also reads long-form "Untracked files:" entries (D2); a git call is the first token and only top-level calls count (N9, N10); the classifier runs after the call, on the record);guard6.mjs(the counted guard also re-checks every file the pre-registration pins (phase C, spotlight gate included));results6.mjs(a comment only (D3));run_reviewer6.mjs(refusals before the call and handling after it only: a record refused at write time keeps its verdict and cost (00e27d7); removing a run dir never throws (bf1308a); the ledger line is written from the spawn result before anything else, and later failures mark the record (refute r2 N1); a failed ledger write goes to a pending sidecar and stops the run (r3 N1); an intent line is written before each paid spawn and cleared after, and every paid run must use the committed ledger (r4 N1, B2); every paid invocation holds an exclusive run lock (r5 N2); a paid run is refused under the Node test runner (r6 B1); a matrix run keeps every call's output (r3 N3); the post-freeze pre-run probe's own ledger kind (D4, N3); and the attempted-per-row check applied after the matrix judge (N6, matrix6-attempts.mjs). The argv builder is unchanged, as the command sha256 equality shows);spend6.mjs(the post-freeze pre-run probe has its own ledger kind, charged to the $60 cap only and made once per frozen pre-registration (D4, N3); a sub-5e-8 reported cost is charged as unknown, a corrupt ledger line refuses every call, and so does a pending (unwritten or intent) line; intent lines are written and cleared around each paid spawn, the clear removing only its own line by an atomic rewrite, and a pre-run probe line without a pre-registration sha counts as the current one (r2 N1, N2; r3 N1; r4 N1; r5 N1, N3));stream6.mjs(records parent_tool_use_id per call and defines a git call by its first token (N9, N10); a matrix run keeps every call's output (r3 N3); the D3 comment; the stream parse and harness-failure rule are unchanged)
Refute r2 N6: an escape row counts as held only if the run ATTEMPTED it (a tool call whose input is that row's step); a row not attempted fails the matrix (matrix6-attempts.mjs, applied after the unchanged matrix judge). Every committed proof and discovery record was re-judged with it: every row was attempted in every run, so no verdict changed. The new -C escape forms (R32-R35, R38, R43) were refused by the allow list itself in every proof run, the sandbox-only run included (they are in each run's permission_denials). The evidence that the OS sandbox is the boundary for outside reads is R9, R45, R29 and R30: they passed the permission layer and git failed with "Operation not permitted". matrix-v6-sandbox6-only-1 was a paid call ($0.5472840) whose record was refused at write time: a home path the client had truncated in a tool output was not matched by the scrub, so the record was not written and its verdict was lost. The scrub was fixed forward and the run repeated (matrix-v6-sandbox6-only-2). The probe of record is matrix-v6-fence6-3.json (sha256 04861e1b0cfd63edc50d009df94060d071b866ebc78c0f642f432f775743af0c).
What the client did, from the recorded stream:
- Yes. Bash(git -C <abs repo> <verb>:*) matches the path as spawned (realpath, under the home cache): git -C <ws> diff, git -C <ws> --no-pager diff and git -C <ws> status --short ran and printed the change. (D2 n60 (C9), n65 (C14), n63 (C12) not refused, marker in output; D1 n58, n63, n61)
- Yes, per sub-command, with no compound rule: git status && git diff, git status --short; git --no-pager diff and git diff --stat || git diff ran; a chain with any non-allowed part (&& cat, ; cat, || cat, | tee) was refused whole. (D2 n62 (C11), n66 (C15), n68 (C19) ran; n51, n52, n58, n53 (R39, R40, R46, R41) refused)
- Yes. 2>&1 and 2>/dev/null are stripped before matching: both ran under the Bash(git *>*) deny rule. (D2 n67 (C16), n64 (C13), n35 (R28) ran; D1 n65, n62, n30)
- Refused. git diff > r47.txt (a redirect inside the workspace) was refused by the client and no file appeared. (D2 n59 refused; calls[0].matrix.info.i47 {refusedByClient: true, fileAppeared: false})
- Every -C to another path was refused by the allow list itself (no rule matches: git -C <other repo>, git -C .., git -C <ws>/.., git -C <temp-root repo>, git --no-pager -C <repo>, git -C <ws>/link). Under an allowed -C prefix an outside path argument to git diff was refused by the client (R44); an absolute outside path to git show ran and was stopped by the sandbox (R45: fatal: failed to stat … Operation not permitted, no canary). (D2 n44-n47, n50, n55 refused; n56 refused (R44); n57 ran, is_error, output without the R45 secret)
- Read: `N<TAB>text`, N unpadded, from 1 (not N→). Grep content mode with -n: `path:N:text`, the path relative to the working directory. Both are stripped by stripReadPrefix / grepLinesFor (fixtures/live-read-grep.json, R4-4 test). (D2 n10 (C18 whole-file Read), n6 (C1), n9 (C17 Grep content))
- Yes. Every tool_use has a tool_result with content text and is_error, refused calls included; refused calls are also in the final permission_denials with their tool_use_id. FINDING: is_error is also set on some git commands that RAN (git status --short, git log, git show, git -C <ws> status), with "Exit code 1" and an appended "zsh: operation not permitted: <tmp>/claude-session/cwd-…" line: the client's own cwd-tracking write fails under the sandbox (the temp root is denyRead). So refusal is read from permission_denials, never from is_error (stream6.mjs, diff-seen.mjs). (D1 207 stream lines, 0 malformed; D2 n14-n17 vs permissionDenials; D1 n38, n61 (is_error with the status output))
The manipulation check: did the reviewer see the change?
DIFF-SEEN iff (a) a Bash git call the client did not refuse printed a whole diff line +<fp> or -<fp> equal to one of the item's fingerprints of that sign; or (b) for an item with added files, a non-refused Bash git output lists the added path or a directory prefix of it as untracked (a short `??` entry, or an entry inside a long-form "Untracked files:" section, never under "Changes not staged"/"Changes to be committed") and a non-refused Read/Grep of that added path returned one of its + fingerprint lines (line-number and path prefixes stripped). A git call is a Bash command whose first token is git; only top-level calls count (a sub-agent's calls never make a run SEEN). Refused = listed in the result's permission_denials; is_error alone is not a refusal (the client sets it on a command that ran when its own cwd-tracking write fails under the sandbox). Otherwise DIFF-BLIND. A harness failure is never DIFF-SEEN.
- A git call is a Bash command whose FIRST token is git (not any command that merely contains the word), and only top-level calls count: a sub-agent's calls (parent_tool_use_id set) never make a run SEEN.
- A refused call is one listed in the result's permission_denials. is_error alone is not a refusal: live, the client sets it on git commands that ran and printed when its own cwd-tracking write fails under the sandbox.
- c018 renames a file with 100% similarity: the pure rename has no hunk and yields no fingerprint; its modify hunks carry its fingerprints (rule a).
- Each counted run record keeps, per tool call, the tool, the scrubbed input, is_error, refused, the output sha256 and length, and the scrubbed output text of git Bash calls and Read/Grep/Glob calls. The scorer recomputes every run's class from those outputs and the committed fingerprints and refuses a record whose stamped class differs.
- Fingerprints: Per corpus item: every hunk line of its patch, trimmed. A + line (modified or added file) is a fingerprint when it has at least 8 characters and its sha256 is not in base-lines.json; a - line when it has at least 8 characters. Added, deleted and renamed paths are listed. A fingerprint matches only a whole trimmed line with the same sign. (sha256
54833bc6f93314539a10aa4f78b280ca1e2d4c5c72fa1ea9a4730288f55dab69; at least 1 and at most 11 per item). Base lines sha25692aeb23a37ce66541d3b586b006d2d04c4027e9569476c063b8e476b3a1ab2d3. - 14 items add a file (
c007,c012,c013,c020,c023,c035,c036,c037,c038,c048,c052,c054,c059,c060); 9 of them add only files.
BLIND runs (harness failures included) at most 18 of the 180 counted runs (10%): the manipulation check PASSES. More than 18: it FAILS, and there is no spotlight whatever the bar says.
The bar: EXP 005's, unchanged
The same spotlight bar as EXP 005: amendment 01's four criteria, thresholds, denominators, hold rule and three-state labels, computed by EXP 005's own spotlightVerdict. The only change is the run-state mapping below.
- Missed drift ≤ 10%: over all RED judgments (30 × 3), the share that is APPROVED or abstained.
- False reject ≤ 25%: over all GREEN judgments (30 × 3), the share that is REJECTED or abstained.
- Self-agreement ≥ 90%: the share of changes where all 3 runs gave the same decided verdict. Any abstaining run makes the change disagree, as in the parent's reviewerVariance.
- Zero patches: the measured run composed and ran from the unmodified 0.34.0 release with no local patch to nina. Harness failures, meaning runs that end without a report for a reason that is not the model's answer, are ≤ 5% of runs.
Missed drift and false reject are judged on the run-level point estimate (n = 90 each). Beside each rate the Wilson 95% interval of the item-level majority (n = 30) is published, because the 3 runs on one change are not independent, with the parent's three-state label computed on that item-level majority: its point estimate and its Wilson interval at n = 30, under the parent's thresholdRule.singleRate. The item-level majority is the parent's reviewerMajority; an abstaining majority counts against the reviewer. A spotlight needs BOTH the run-level point estimate within the threshold AND an item-level label other than "refuted". With 30 RED items the interval cannot establish 10% or less: 0 of 30 has a Wilson upper bound of 11.35%.
All four criteria must hold for a spotlight. A partial run (cap or hang-stop) gives no spotlight. Failing any one criterion means no spotlight; the results are then published neutrally with the same attribution. The spotlight describes the harness as measured, meaning its reviewer on mechanical architecture rules, and nothing it wasn't tested on.
Harness failure: A counted run that ends in a timeout, a non-zero client exit, no parseable final `type:"result"` line, or an unparseable NDJSON line, is_error: true, or an empty result. A run with a completed result and no verdict line is a model abstention, not a harness failure. is_error here is the final type:"result" line's only. A tool_result's is_error (a refusal, a failed git, or the client's cwd-tracking artifact) never makes a run a harness failure.
A BLIND run, harness failures included, is scored as an error in missed drift and false reject, at run level and in the item majority (where it counts as the wrong answer), and as a disagreement in self-agreement: its decision is treated as no decision before EXP 005's formulas run.
- Hook errors are recorded and their rate published beside the bar; they are not a criterion (as in EXP 005).
- The same rates over the diff-seen runs only are reported beside the primary figures, never in their place.
- A partial run (the spend cap, the hang-stop, a crash, or coverage other than exactly runs 1..3 of all 60 items) decides nothing.
- p90 (and any percentile) is results.mjs percentile: numpy's linear interpolation.
- Each rate is published with its three-state label: refuted, passes, not established at this N, passes.
Automatic and pre-registered: a spotlight-decision record with held: false is committed if and only if the bar PASSES, the manipulation check PASSES, and the independent results refute SHIPs; otherwise held: true, naming the condition that failed. A founder decision is asked only if a refute raises a doubt this rule does not decide.
What a PASS publishes. On a PASS (the bar PASSES, the manipulation check PASSES, and the committed EXP 006 spotlight decision says held: false), the home page's "Tools leaving the factory" list gains nina's entry, next after 002 Laya, in the same shape as the Laya entry (number and category, a title linking to the evidence, one paragraph, links, an attribution note, a spec list): nina, harness orchestration for Claude Code, by Marcos Schulz (xhulz), https://github.com/xhulz/nina, with the attribution "used with the permission of its author, as confirmed by Odin Labs", and links to the EXP 006 measurement, to EXP 005 (#spotlight) and to the upstream fixes opened by Odin Labs (xhulz/nina#39, merged; xhulz/nina#41, stated as its live state at publish time: merged or open). Every figure in it is rendered from the EXP 006 results record at build time. It takes the list's next free number, 005, the slot EXP 005's gated nina card held; there is only one nina entry. What a FAIL publishes. On a FAIL, or when the spotlight is held for any reason, there is no entry, and the results page says plainly which condition failed (the bar, the manipulation check, the refute, or a partial run). WHETHER the entry shows is decided only by experiments/nina-changes/spotlight-gate.mjs, pinned in files below and re-checked by the counted guard. Its one entry point, gateOpen(root), owns the paths of the results record, the spotlight decision and the reviewer run record, reads them, and opens only for a real, complete, counted EXP 006 measurement under THIS frozen pre-registration: the results record is kind results, experiment EXP 006, not a fixture, not a rehearsal, not partial, made under the pre-registration sha frozen in freeze.mjs (which must be set), and passes EXP 005's publication check; the decision names the results record AND the reviewer run record by sha256; the reviewer run record is a complete counted EXP 006 reviewer run (kind gate-run, experiment EXP 006, gate reviewer, mode counted, not partial, not a fixture or a rehearsal) made under the same pre-registration, with the same not-before and code as the results, its calls exactly the runs the results scored (as id, run, decision, harness failure and diff-visibility class, in order) and as many as the manipulation denominator, and a parseable end time, and the Measured date comes only from it; and the bar PASSES AND the manipulation check PASSES AND spotlightEligible is exactly true AND held is the boolean false. A missing, unparseable, unbound or failing record hides the entry. It carries EXP 005's card gate (spotlightShown) byte-identical, with a slice test. HOW it looks is scripts/nina-changes-spotlight.mjs, the markup, not pinned: it holds no record path and no gate logic and renders only what gateOpen returns (its figures are read from the results record gateOpen hands back, and the PR states from the committed upstream.json), so the results can feed it without an amendment.
Spend, the pre-flight and the clock
- A cap of $60 on every paid call of EXP 006, with a $10 ceiling on everything before the counted run. Every paid call of EXP 006 (matrix probes, the dry run, the counted run) appends one ledger line, cost to 7 decimals from the client's total_cost_usd (API-equivalent). A call whose cost is unknown is charged its upper bound, max(the largest EXP 006 call so far, $0.60), marked upper-bound; never a $0 line. Before a call the runner refuses it when the recorded total plus a reserve of max(largest call so far, $0.60) would exceed $60, and a pre-counted call when the pre-counted total plus that reserve would exceed $10: enforced in code (spend6.mjs). Every paid run (practice, probe, pre-run probe, counted) appends to this one committed ledger; only fixture and rehearsal runs, which make no paid call, may use another. Before each paid spawn an intent line (call id, kind, its upper-bound cost) goes to spend-ledger.pending.jsonl and is cleared once the real line is written; a leftover intent or a failed write keeps that sidecar non-empty and the runner refuses every further call until the operator reconciles it at the upper bound; if the intent cannot be written, no call is made.
- Paid so far (7 matrix probes, none counted): 0.6545558 + 0.5596686 + 0.6258170 + 0.5681038 + 0.5472840 + 0.5811820 + 0.5455714 = 4.0821826 dollars.
- Reserve $0.6545558; dry-run headroom $5.2632616. The headroom holds only while no call exceeds the current reserve: the reserve is max(the largest call so far, $0.60) and rises with the largest call, so a dearer call lowers the headroom for the calls after it.
- D4: the separate post-merge probe is dropped. The measured run's own pre-run matrix probe, made after the freeze on the frozen runner, is the post-freeze proof; it has its own ledger kind (prerun-matrix) and is charged only against the $60 cap, never the $10 pre-counted ceiling. Pre-freeze probes and the practice dry run stay under the $10 ceiling. The real headroom for the dry run: $10 − $4.0821826 spent − $0.6545558 reserve = $5.2632616 before the last allowed call; the reserve is max(the largest call so far, $0.60), which is also the unknown-cost upper bound, so it cannot be lowered. Reaching the ceiling stops the dry run and asks the founder: bundle 2 may then complete fewer than its 18 practice runs, and its acceptance (every completed run diff-seen and hand-read) is judged on the runs completed, with the shortfall reported; whether that is enough to go on is the founder's call.
- Reaching the $10 pre-counted ceiling stops the run and asks the founder (raise the cap or trim scope). Before the counted run: if spent + 180 × p90(dry-run per-run cost) + one pre-run probe + $0.60 > $60, the founder is asked first.
- Answer-key pre-flight, EXP 005's mechanism with EXP 006's pins file and not-before: one counted pre-flight record written before the measured run (runner-guard.mjs --preflight --mode counted --write-record --pins-file experiments/nina-changes/runners.sha256), ended at most 2 h before the first counted call and after the not-before, bound to that runner code and commit, and committed with the results; no commit between the record and the last counted call's start.
- No counted reviewer run starts before the merge time of the odin-rnd pull request that adds this file; that time is recorded after publication, frozen with this record's sha256 in freeze.mjs, and every counted call must start after it. Practice runs and matrix probes on canaries do not wait for it.
What this does not establish
- Two layers: the text deny rules are best-effort (the client matches a normalised command); the OS sandbox is the boundary: reads outside the repository within home and the temp roots are denied (the sandbox's denyRead and its home block; R9 and R45 read a file outside the repository and got "Operation not permitted"); writes are confined and there is no unsandboxed retry. Nothing is canaried anywhere else, so the matrix tests the sandbox where the answer files could be.
- Untested residual: git diff --no-index given an in-workspace symlink to an outside file (and /dev/null) was not run live. It is refused by the layer-1 text rule on --no-index, and R45 shows the sandbox resolves the real path of what git reads; it is stated as untested, not proven (no new live row was added; matrix6 is unchanged).
- Untested in the matrix: git forms with an environment prefix (GIT_DIR=…, GIT_EXTERNAL_DIFF=…, GIT_CONFIG_PARAMETERS=… before git). They are believed refused because no allow rule's prefix matches a command that starts with an assignment, and whatever they ran, reads outside the repository are stopped by the sandbox (R9, R29, R30 and R45, and the sandbox-only run). No live row was added; this is stated as untested, not proven.
- git diff -O<file> (an orderfile) is allowed under the git diff rules and makes git read that file to order its output; it prints none of the file's content, so at most it is a side channel (whether an outside path exists or parses). Documented as a residual; no live row was added (no paid call).
- The attempted-per-row check compares each row's recorded command with its registered step after the client's own normalisation only (a 2>&1 or 2>/dev/null redirection stripped). An honest rephrasing beyond that marks the row not attempted and fails the pre-run probe, which is the only one allowed under this pre-registration: the consequence is a dated amendment with a new not-before (then its own pre-run probe), not merely a question to the founder.
- A stream with a malformed line is charged the upper bound even if a final result line reported a cost (conservative: the line cannot be trusted).
- Sub-agents: a run can spawn a sub-agent whose own tool calls appear in the stream with a parent_tool_use_id. Only TOP-LEVEL calls count toward DIFF-SEEN (a sub-agent's calls are recorded but never make a run SEEN), so a run whose change was read only by a sub-agent counts BLIND.
- Reviewer only: Jev, Laya and the cascade are not re-run; this measures nina 0.34.0's reviewer on mechanical architecture rules and nothing else about nina.
- Same corpus, inputs, labels and rules as EXP 005, so the results compare; the corpus is public, and the reviewer is fenced from the answer files by the sandbox and the answer-key pre-flight.
- Prompt confound: the prompt says "the change is everything that git diff shows", which is false for the 14 items that add a file (9 of them add only files): git diff does not show untracked files. The prompt is EXP 005's, unchanged, so the results compare.
- Client artifact: Claude Code 2.1.280 marks some git commands that ran (git status --short, git log, git show) is_error, with "Exit code 1" and a line for its failed cwd-tracking write under the sandbox. It likely affected EXP 005's tool results too; EXP 005's records keep no tool output, so this cannot be checked (its classifier never read tool-level is_error). EXP 006 reads refusals from permission_denials and never from is_error.
- Known false-BLIND forms: the classifier does not recognise a change shown only through git diff --color=always, git diff --word-diff, git diff -R, a coloured git status (-c color.status=always), git status --porcelain=v2 or -z, or a long-form status run from a subdirectory (its ../ paths). A run that saw the change only that way is counted BLIND: an error against the bar and the manipulation check, never toward a spotlight. Each form is pinned BLIND by a unit test (documented behaviour, not a silent gap).
- The runs are sequential, on one machine, under whatever load it has; latency is recorded, not judged.
- The spend guard's pending sidecar is per worktree: bundles 2 and 3 run from ONE worktree (named in experiments/nina-changes/README.md), one after the other. Two runners at once could each pass the spend check and overshoot the cap by one call, so every paid invocation holds an exclusive lock file (experiments/nina-changes/run.lock, created O_EXCL) for its whole run and a second one refuses; a stale lock is removed by the operator after checking. A reconciled pre-run probe line without a pre-registration sha counts as made under the current one (fail closed).
- Paid calls before this record: 7 matrix probes, $4.0821826, itemised in spend.calls; none is counted in any result.
The page widths were checked two ways: a real headless Chromium render (playwright-isolated, served from the built site) of the home page, this note and the EXP 005 note at 375 px and 320 px, finding no horizontal overflow (document scrollWidth equal to the viewport); and a static repository test that no unbroken run in the note is wider than the column unless the stylesheet lets it wrap.
Provenance
This page is rendered from preregistration.json, the committed record at experiments/nina-changes/preregistration.json, whose sha256 is 41efb90da633b42acca619ab4d761e15e608ca06e496bafbc73ccfd9df03b98a. The build publishes that file byte for byte, and a repository test re-renders this page from it and fails if they differ. A validator rebuilds the record from the files on disk.
No counted run has started. The results, when they exist, will be judged against this record as published, and reported as they come out. Measured — bar met, nina in the spotlight, 01 OCT 2026: see the results.