Repository navigation
Confirmatory runner: lanes, one engine pool per model, admission and the ceiling - #244
Merged
Merged
Conversation
… ceiling scripts/confirmatory_run.py runs the frozen pre-registration's run procedure on the treatment harness (6b00a6f's tool_pilot_argon.py, unchanged) and writes the run records the confirmatory report checks: - two lanes per model, households alternating in draw order, each household's arms in its drawn order, then reruns in passes; - one ToolPool per model with no features of its own, each call framed with its arm's features; - admission against each model's cap and reserve, waiting while a pass's reruns are undecided; - the hard ceiling over every model's spend, through the harness's Spend; - the failure classes and actions, with operator pauses, harness and inconclusive stops, and replaced attempts saved before their reruns; - the 30-second host log and the guard's and power log's lines. Tests follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Unit tests for the streams, arm strings and features, the classes and actions, the host evidence and the ceiling. Fake runs (a fake provider, pool and host log, on a fake board, with the windows scaled down) plant reruns, host trouble, an operator pause, a harness stop, the inconclusive stop, a refusal, the hard ceiling, a raising attempt, pool restarts and an interrupt. run_problems recomputes every rule of the run procedure from the files the runner wrote, and a property test checks it over random failures and spend. Two tests run the harness's own conversation under the runner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A model whose lanes never decide, because the ceiling was crossed before its first admission, wrote no admission log, and the confirmatory report reads a missing log as a lost one. The runner now creates each model's run and admission logs when it writes the model's manifest. Found by the differential test in the study's scripts repo, which runs this runner and checks its records with the report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
force-pushed
the
v2-confirmatory-runner
branch
from
October 25, 2026 08:11
7d8eaec to
aeff9a9
Compare
…cle fixes The wall clock steps back by up to about 0.1 ms between threads on the run's host (40 backward steps in 190 million readings over 12 threads, the largest 91 us). The run records order attempts, stops, admission decisions and the ceiling's crossing by their times, and the confirmatory report compares them exactly: an attempt's end and the next start in its lane can be microseconds apart, so a step back could make a valid run fail the report. `clock()` gives time.time(), never earlier than a time it gave before, and each of those times is taken under the lock that orders it. A test reads the wall clock up to 0.2 ms early everywhere; without `clock()` it fails every time (attempts overlap, a household runs before its admission). The test oracle had three faults, two of them exposed by the full suite: - A household a stop cut short was taken to finish at its last attempt. The runner counts it under way until the stop, as the report now does. A new test makes the case deterministic (it failed the old oracle with the flake's message, spend 0.02 vs 0.03). - The scaled host-log gap was 90 ms, and under a load of 12 the sampler stalled past it, so an untouched run got host reruns. The gap is 0.5 s in the tests, and the stall tests stall 0.8 s. - The oracle allows the harness's raw request times a second outside their attempt, as the report does. 121 tests pass, 8 runs of the full suite in a row at a load of 12 to 146. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
force-pushed
the
v2-confirmatory-runner
branch
from
October 25, 2026 08:17
aeff9a9 to
808ccc8
Compare
… chains and anchors Answers runner-r1 (R1-R8) and the runner side of the pre-registration and report reviews: - A failed read of the guard's or the power log is tried five times; one that still fails is a runner error, which stops every model's lanes after settling and writing what had ended. A failed memory-pressure reading is no sample. - An attempt still running at an hour is abandoned: a harness-error record (AttemptDeadline) with its spend so far, a stop of cause deadline, and its later spend in late-cost events. An attempt that raises keeps the spend it counted. - Admission takes its time and checks the ceiling under the ceiling's lock. - A resume is accepted only if written after its pause began. - The signal handler only notes the signal; the main loop stops the lanes. - A sampler whose samples fail for five minutes, or whose thread ends, stops every model. - --workers must equal the design's. - Every stop names its cause. The error classes add the litellm subclasses the harness groups by isinstance, explain traces count as calls, and a Timeout on the escalated request after a truncated turn is class model. - Run, admission and host logs are hash chains; anchors.jsonl records every log's head every 15 minutes and at the end, and the design's anchor command (confirmatory_anchor.py) commits and pushes it. - In-flight records are fsynced before their rename. 189 tests pass (tests/test_confirmatory_run.py, tests/test_confirmatory_anchor.py). 19 mutants of the new rules each fail a named test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The docstring and EXCERPT_LEAD's comment said "the booking"; the code keeps the guard's and power log's lines from a day before the host log's start. No behaviour changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test sent its interrupt on a 0.15 s timer, and at load 49 the run had not started the household's first attempt by then, so no attempt was cut short and the test failed. The interrupt now arrives from inside that first attempt, which finishes only once the main loop has stopped the lanes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Answers the first part of review runner-r2: - The ledger can close. Ceiling.charge counts spend with the lock held and, once close() has run, counts nothing more and keeps the amount apart as unrecorded_usd. AttemptSpend now changes its totals under the ceiling's lock, so the attempt's totals and the ceiling's always agree. - The sampler's failure timer starts on every caught sampling exception (finding 3), so a reader that keeps raising stops the run after the silence limit instead of never. - Guard-log lines carry no offset, so guard_times reads each as both readings of a repeated or skipped local hour (New York repeats 01:00-02:00 on 2026-11-01). A pause opens at its earliest reading and closes at its latest; excerpts keep a line if either reading falls in the window. - ANCHOR_RETRY and ANCHOR_PUSH_TIMEOUT are defined for the anchor changes that follow; LATE_JOIN becomes one overall deadline. The suite has not run on this commit: the next commit runs it and finishes the round (stop_all, end_attempt, runner errors, startup, finish(), the resume race and the anchor push). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Answers review runner-r2 and finishes f210b6e: - Set and notify every model's stop state before event I/O. Retain ended attempts before pause/stop writes; recover their classes and final records best-effort before runner-error stops, and block new starts during recovery. - Finish against one abandoned-request join deadline. Close pools and host before the ledger, then record late costs, crossings and interrupts before the final anchor. Snapshot unresolved requests under the ledger lock and record their unknown future spend explicitly as a runner error. - Confirm persistent sampler exceptions stop the run and a good sample clears the failure timer. Validate a resume against the current pause under the condition that clears it. - Poll pool startup with supervision and process pending stops before every lane launch. Record startup/restart spans for the report's idle calculation; drain supervisors on runner errors before closing their host and ledger. - Kill the whole anchor command process group at ANCHOR_PUSH_TIMEOUT. Retry failed pushes using ANCHOR_RETRY and sync each outcome, anchor timestamp and push interval. Surface push-log I/O failures and retain final local coverage. - Exercise disk-error recovery, late cleanup spend, unresolved requests, persistent sampler errors, the resume race, startup signals and slow pools, failed pushes and real child/grandchild timeout termination. The report must subtract validated setup spans from lane idle time and compute remote exposure from successful pushed snapshots. Local cadence alone cannot support the draft's claimed fifteen-minute maximum; neither report nor draft is changed here. Co-Authored-By: GPT-6.1 Sol (Subfleet) <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Answers review runner-r3 findings 2, 4, 5 and 6, and orders push times: - Push a synced, atomically installed snapshot of the anchors file. Give each push its own path, including after a thread is abandoned, and keep the remote record named anchors.jsonl. - Try the final push up to three times per set of heads, a minute apart, writing a new anchor for each try without changing the covered logs. - Bound pipe draining after process-group termination and every push join. Record a stuck thread once as an error and reject its late outcome. - Ignore signals before the last cleanup check, then log and anchor a first signal that arrived in the gap. Keep first and second signal handling active throughout retries. - Take anchor cadence and push intervals from the runner's ordered clock. Exercise overlapping pushes, retry exhaustion, escaped descendants, abandoned threads, cleanup signals and a backward wall-clock step. The report's record fields and outcome names are unchanged. Push snapshots are copies, not records; the draft should distinguish final retry exhaustion from the ongoing run's retry cadence, and retries after newly logged signals. Co-Authored-By: GPT-6.1 Sol (Subfleet) <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Answers review runner-r4 finding 1 and tests the remaining clock and retry spacing requirements: - Hold an exclusive, non-blocking lock in the anchor clone's .git directory through the push. Refuse busy helpers and snapshots that do not extend the clone's anchors byte for byte; allow equality and never force-push. Leave a stale index.lock to fail normally. - Keep worker retirement and the next push, but say the retired command may still be running. A helper holding the lock makes later pushes fail as busy; final anchors left local are a deviation. - Exercise older helpers after newer pushes, shared-clone retirement, lock contention, interior edits, equality and stale git locks against a temporary clone and local bare remote. - Test anchor_due across a backward wall-clock step and measure each final retry from the preceding try's end, with a controlled clock. - Clarify that final retries wait a minute after a failed try ends. First and second signal handling stays active only until cleanup ignores signals before its last check; signals during re-anchoring after that check are ignored. State that write/fsync under _pushing can delay supervision and retirement beyond the thread join bound. The report's record fields and outcome names are unchanged. Retired-worker output now identifies the surviving-command risk; helper refusals are failed pushes. The clone's persistent lock file is auxiliary, outside run records. Co-Authored-By: GPT-6.1 Sol (Subfleet) <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Answers review runner-r5 finding 1 and fills the retry and conversation coverage gaps: - Pass the clone's flock descriptor to every Git command. A surviving Git writer, hook or remote helper retains the lock after helper-only death; later helpers still refuse busy clones and non-extending snapshots. Disable fsmonitor startup, whose daemon closes inherited descriptors. Keep ordinary, non-force pushes and leave Git's own index.lock alone. - Kill the real helper after Git has entered pre-commit, pre-push and local pre-receive hooks. Check busy refusals, inherited hook descriptors and unchanged clone file, index, HEAD and remote, then newer pushes and every remote commit's byte-prefix extension. Test that configured fsmonitor is never started. - Pin the registered 900-second anchor interval and 60-second retry wait independently of production constants, citing draft line 218. - Compare fake OpenAI Responses and Gemini JSON conversations against the unchanged treatment harness, including complete requests and calculator inputs. Exercise both through the runner's lanes. Use the existing Gemini 3.1 JSON profile; both confirmatory Geminis retain their tool contracts. - Describe surviving Git lock holders and advisory/custom-command limits. The report's record fields and outcome names are unchanged. Busy refusal output now identifies a helper or Git process as the holder. The existing clone lock remains auxiliary, outside run records. Co-Authored-By: GPT-6.1 Sol (Subfleet) <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Answers review runner-r6's two P3 items: - Narrow the fsmonitor claim to disabling normal index-triggered startup. A configured hook can explicitly start the descriptor-closing daemon; configured background clone writers must retain the locked descriptor without unlocking it. The flock is local, while an external remote's safety rests on the byte-prefix check and ordinary non-force pushes. - Describe credential-cache and other descriptor holders keeping later helpers busy for their lifetimes, including the default 900-second cache. Every try while the lock is retained, including all three final tries, fails as busy and is reported as a failed push. - Block real Git clean filters during add and status, kill only the helper, and check later busy refusals leave file, raw index, staged contents, HEAD and remote history unchanged. Release the writer and verify newer snapshots push with prefix-preserving history. - Double-fork a hook descendant into a new session. Let its hook and helper finish, check the detached holder still prevents later pushes, and verify pushes succeed after it exits. Check each new regression against a temporary helper with inheritance removed only from its target command. Production behavior, report fields and outcomes are unchanged. This adds no dependencies and leaves the report implementation and draft untouched. Co-Authored-By: GPT-6.1 Sol (Subfleet) <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Enumerate saved histories and calculator evidence explicitly. Missing optional folders retain empty inventories; covered listing and stat failures must name their paths. Preserve sorted JSON histories, saved spend and attempt decisions. Guard exclusions metadata, model discovery, archive overwrite checks, summaries, calculator records and host logs, and the preregistration hash read. Read baseline exclusions and the eligible pool once each. Their hashes must bind the exact buffer decoded with strict UTF-8. Guard both calculator engine reads with the metadata sidecar path. Preflight retains its collected problems, and main prints every problem and returns 1. Valid board engine checks and provider execution retain their existing paths. Co-Authored-By: GPT-6.1 Sol (Subfleet) <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explicitly list each candidate calculator stage before checking its literal households child. A covered failure listing that parent must become a preflight problem naming the stage folder. Preserve the same household path, record filter and early refusal once saved calculator records are found. The final regression run exposed this missing parent listing after the first implementation: 39 cases passed and the denied-stage-listing case still failed. Keep the original failing regression and verify the corrected inventory and preflight paths. Co-Authored-By: GPT-6.1 Sol (Subfleet) <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three test modules added for this round take descriptive names: test_confirmatory_unreadable_evidence.py holds the folder, listing and stat refusals; test_confirmatory_input_reads.py holds the single-buffer hash and decode checks for the baseline exclusions and the eligible pool, and the saved-variables control shared with the report; test_confirmatory_board_engine.py holds the board-engine refusals through preflight and main. The control's imported report module drops the round number from its name too. No test or production code changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… per record Runner review r16 found two gaps. A no-tools model folder, a calculator arm or a calculator stage that a listing returned was dropped when its stat failed with ENOENT: a dangling link (an optional model's folder on an unmounted volume) or an entry removed after the listing. The report refuses such an entry. listed_is_dir now refuses every stat failure of a listed entry, at startup, in stage_problems and in the preflight. A missing root, households folder or attempts folder, which the code names itself, still reads as empty. history decoded each saved record with one read and hashed it with another, and Stage.archive hashed the runner's final again before the move. read_record now returns the digest of the bytes it decoded; an archive's link carries that digest, and the history keeps each final's digest (the bytes read at startup, or the bytes write_json wrote). Before the move, archive refuses a final whose bytes no longer hash to it. history now lists a model's finals before its attempts folder, as the report does, so a households folder that cannot be listed or searched is the folder both programs name (report review r15 finding 4). New tests: a dangling or vanished model entry (startup and preflight), a dangling or vanished calculator arm or stage (preflight), a file at or a stat failure on an optional model's households or attempts folder (startup and preflight), a second read of an archive or a final that differs from the hashed bytes, an archive link equal to the digest its final was read from, and finals changed after the runner read or wrote them. The board engine test now requires the preflight guard's own line and every saved-treatment line beside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In suites r10, three of 0ae6df5's new cases failed in all three runs: - test_archive_refuses_a_final_changed_after_the_runner_wrote_it: Stage.archive created the attempts folder before it checked the target and the final's digest, so a refused move left an empty attempts folder in a history that had none. Both refusals now come first, and the folder exists only when the move proceeds. - test_saved_record_hash_and_decoder_share_one_buffer[archive] and [final]: the test hashed the files itself after installing the read counter, so its own read counted as a second runner read. It now hashes them before counting. The runner read each record once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… files
Review runner-r17 (sol61, REQUEST_CHANGES) found two gaps between the
runner and the report and three smaller items. This commit fixes each.
- The calculator preflight now refuses what the report refuses.
stage_problems refuses any model's archive that no final lists, and
any final whose action is rerun or pause-rerun. That covers optional
models, and cheap-model archives outside the draw. Startup recovery
still resumes both states.
- Saved records now go through the report's descriptor guard.
read_regular opens with O_NONBLOCK and refuses a descriptor that is
not a regular file, then reads through that descriptor. Before this, a
FIFO with no writer could hang startup while it held the invocation
lock. The archive move's digest checks use the same reader.
- The archive move hashes the moved bytes again after the rename. If a
write landed between the first hash and the rename, it moves the bytes
back and refuses. The docstring states the limit that remains: the
runner cannot see a write to the same inode during either hash or
after the second. Every later reader hashes each archive against its
link.
- New tests:
- a FIFO or device in place of a final or an archive, at startup and
preflight, bounded by a writer that releases a blocked open after
5 s;
- unfinished optional and outside-draw histories;
- a write between the digest check and the rename;
- a target collision that keeps both files;
- a digest refusal that leaves no attempts folder, and the missing
folder after a target-stat refusal;
- the changed-written-final case across every cheap model, which pins
the other models' continuation.
- The optional no-reply control now expects the preflight to refuse its
final, which owes a rerun.
- The read-counting helper also counts read_regular. It patches that
reader only where it exists, so the same cases run against c2ca2dc.
Against c2ca2dc, 14 of the new cases fail: 8 special-file, 1 move-back
and 5 unfinished-history. The collision, folder and continuation cases
pin existing behaviour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI runs `uv run ruff check .` and `ruff format --check .` with the locked ruff 0.15.2. CI has not run on this draft. Run locally on c2ca2dc's tree, both fail on files this PR adds: four files needed formatting, and four lines ran past 88 characters. This commit formats those four files and rewraps the one string the formatter could not split. Every changed file parses to the same AST as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mutant check of 274e1c7 found one fault that the tests missed. Its digest read in Stage.archive used the blocking sha256_file. The new case replaces a prepared final with a FIFO after startup. The archive move must refuse it at once by name, within the 5 s bound, and leave no attempts folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he abandon suites-r12's first runner pass at 0193811 failed test_an_attempt_past_its_deadline_is_abandoned_and_stops_the_model with 0.02 == 0.01, and the next two passes held 730 passed. The test gave each turn 0.2 s against a 0.3 s deadline, so its second turn spent at 0.4 s, and it expected the waiting thread to abandon the attempt inside that 0.1 s. A wake 0.1 s late put the second turn's cost in the attempt's record, which is the spend so far that the runner's rule asks for. The runner was right and the test's expectation assumed a wake time. A plugin that delays the abandon by 0.25 s reproduces the same assertion at the same line. The first turn now spends at once and the second waits for the abandon itself, through a new `hold` key in FakeProvider. The deadline rises to 1 s and the other model's attempts to 0.2 s, which leaves 0.8 s of room against that model reaching its own deadline. The assertions are unchanged. The test passed 25 of 25 runs alone and 25 of 25 with the 0.25 s delay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eline rules Answers review runner-r18. stage_problems dropped the cost history returned and never compared it with spent_before, and the preflight never read the baseline. Calculator spending could therefore start from an understated ceiling, or on a baseline the report refuses, and the report would then withhold the results. stage_problems now sums every model's validated cost and refuses a spent_before below that sum by more than MONEY_TOLERANCE, the report's value. It refuses an unusable value by the report's money rule: finite and nonnegative, with numeric text and booleans counting as numbers there too. check_baseline is the startup check moved unchanged, so startup and the preflight share one rule. Tests: a full preflight against spend in a final, an archive and an optional model; each unusable spent_before; and each baseline fault. The second hash after a rename gets its own FIFO case, because the earlier case attacks the first hash. Two execution cases fail the second hash and the move back, then check that the other models finish, the lock is released and startup recovers the archive. The deadline test now records the join's timeout: its hold on the second turn hides when the abandon came, so a half deadline passed it. The ceiling test waits for the crossing where it slept 0.3 s against an unbounded competitor. The archive docstring now limits later digest checks to surviving links: after an interrupt, startup rebuilds the last archive's digest from the bytes then present. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Answers review runner-r19 and item 3 of confirmatory-report-r18. The preflight added each cost to a running total, and the report grouped its total by model, so the two programs rounded differently. A spent_before at the tolerance passed one and failed the other, and $2 of saved spend could round away beside a $1e16 cost. history now lists each saved cost, and exact_spend takes their correctly rounded sum, the same in any order and grouping. The report takes the same sum. An intermediate overflow means a total past every float, which the comparison refuses. Startup's spend uses the same sum. Tests: every order of two sets of costs at the tolerance, costs a running total rounds away, and a total past every float; a spent_before given as numeric text or a boolean, which counts as its number in both programs; and a design without spent_before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review runner-r20 found that restoring a running total at startup alone would pass every test. A new case saves costs whose running total stays under the cap and whose exact sum reaches it, and asserts that no owed attempt starts and that the invocation lock comes free. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s release Review runner-r21 found two gaps in the startup test. Rounding each model's costs first escaped it, so a second test saves costs whose subtotals fall one float under the cap and whose exact sum reaches it. The test's lock assertion read bookkeeping, so both tests now take the lock from a second open. `exact_spend`'s docstring limits the claim to a flat list of costs, in any order, and to histories that pass validation. Production code changes only in that docstring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review runner-r22 found that startup's overflow rule had no test. Replacing `exact_spend(costs)` with `math.fsum(costs)` at startup alone would have raised a raw OverflowError and passed every test, because the existing overflow cases reach the preflight or refuse one invalid cost. A new test saves two valid 1.5e308 finals and requires that startup takes infinity for the total, stops every owed attempt and frees the lock. The helper takes the expected total as an argument. Production code doesn't change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI's test job failed 22 confirmatory tests on Linux at 3846666; they had passed on the Mac that ran every earlier suite. Fourteen reached a preflight check that compares the installed policyengine-us with the board's engine, and the lockfile installs 2.41.0 where the fixture board names 2.60.4. Eight reached host_power, which calls macOS's pmset. World already fakes the board's engine, so it now fakes the installed version and the power reading too. Its tests no longer read either from the host. A test that wants a different reading still sets it after World, as the lid-closed test does. The eight stubs in the unreadable-evidence tests and the ones in pool_design and engine_fault repeat World's and go. Production code does not change. A pytest plugin that hides pmset and reports 2.41.0 reproduced CI's 22 failures on this Mac at 3846666, by name. With this commit all 772 runner tests pass under that plugin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
World now reports the installed engine and the power reading, so no test could see the checks that read them, and no earlier test had held those checks with the real values either. An installed engine other than the board's must now refuse in the preflight twice, in its own check and through the no-tools treatment check, and again in main's final check, which must keep the preflight's problems. Each test requires that no provider is called. host_power gets its first test, on the four mixes of power source and lid state, with the text pmset and ioreg print on this Mac. Production code does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #240 (
v2-microcosm-batches). Rounds 6 and 7 agreed on the calculator stage at 9b30a77 and 4002f8e. Commit 047c868 adds the no-tools stage's script. Rounds 8 to 10 asked for changes, made in 08d7b18 and 5a9735b, then in c60086b, 248e777 and 4ce7f8e, and then in 2f795ea, 6597adb and 9acc920, which round 11 reviewed. Round 11 asked for four P3 changes (watcher-startup and descriptor-order tests, three wordings), made in a1114fe (tests only) and 89dd946 (docstrings and comments only), which round 12 reviewed. Round 12 asked for one wording, in the pre-registration draft. The report's own round-11 review found that a record nested deeply enough to make Python's JSON decoder raiseRecursionErrorescaped its reader; this runner's reader had the same gap, closed in 4dafb83, which round 13 reviewed. Round 13 asked for three changes: the board files that the harness's loader decoded for the script escaped its refusals or lost their names, a saved cost too large for a float escaped with no path, and the pre-registration draft needed to say that the named refusals exclude running out of memory. Commits 040d206 and 6928b06 made the first two, the draft made the third, and round 14 reviewed them. Round 14 asked for four changes: the harness's reread of the board, after the script's own checks, could still escape with a raw I/O error or an unnamed recursion error; the script accepted saved costs that the report refuses (true,"0.25",null,[]and a missing cost); the differential test's nesting case decoded on the pinned Python; and this description overstated how many new cases use a real board. Commit 8b6c8f7 guards the reread and adopts the report's cost rule. Commit 0300257 applies the report's other checks of a saved no-tools record, which a comparison of the two programs turned up. The new checks refused the bare no-tools finals that five of the calculator runner's preflight tests wrote, so those tests stopped at each model's first household before their planted fault; commit 4fb80d9 builds the finals through the harness instead. The study's scripts repo fixes the differential test, and this description now counts the cases correctly. Round 15 reviewed them. Round 15 asked for four changes: a history folder the script could not list looked empty, so an unreadable optional model's history passed the preflight and added no spend; the script hashed the baseline exclusions file in one read and decoded a second; the calculator runner's preflight andmainread the board's engine with no guard, so a malformed sidecar escaped and the preflight lost the named problem the no-tools check had recorded; and this description and the draft claimed more than some tests and guards show. Commit 5e334d9 adds the regressions, d31c1d7 lists saved evidence directly, reads the baseline exclusions file and the eligible pool once and decodes the bytes it hashes, and guards both engine reads, and cccc381 adds a listing the preflight still skipped; 4d93b40 names the new test modules for what they check. Round 16 reviewed them. Round 16 asked for changes: an entry that a listing returned, and whose metadata read then found nothing (a link to nothing, or an entry removed after the listing), still looked absent, so an optional model's folder on an unmounted volume passed with none of its spend; the script decoded each saved record in one read and hashed it in another; some faults would pass every test; and this description and the draft claimed more than the tests cover. Commit 0ae6df5 makes the code changes and adds the tests, and c2ca2dc fixes the three of its cases that failed in the suites: the archive move created theattemptsfolder before its refusals, and one test counted its own read as the script's. Round 17 reviewed both. Round 17 asked for changes: unfinished optional histories passed the calculator runner's preflight; a saved record that was no regular file passed or blocked; a final could change between its hash and its rename; two archive refusals had no test; and this description and the draft overstated what a refused move leaves behind. Commit 274e1c7 makes the code changes and adds the tests, d3673c3 formats four files with the ruff that the repository's lock pins, 0193811 adds a case for the descriptor guard before the move, and a8226dd makes the deadline test's second turn wait for the abandon, because a loaded host's late wake failed that test once. Round 18 reviewed all four and asked for changes: the calculator runner's preflight compared no saved no-tools spend withspent_before, and it never read the baseline, so either could start a stage whose results the report would withhold; several faults passed the tests; and this description and the draft overstated later digest checks. Commit 4441364 makes the code changes and adds the tests. Round 19 reviewed it and asked for changes. The preflight and the report grouped and rounded saved spend differently, so aspent_beforeat the tolerance could pass one and fail the other. This description's invariant also claimed more than the differential covers. Commit ceae93f sums every saved cost withmath.fsum, as the report's c714369 does, and adds the tests. Round 20 requested narrower wording. It also found that a running total restored only at startup would pass every test. Commit 3c80377 tests that startup stops at the correctly rounded cap. Its production diff changes only a docstring. Round 21 reviewed it and asked for narrower wording here and one count. It also found two test gaps: a startup that rounded each model's costs first would pass, and the test read the lock's bookkeeping. Commit 58ba64b tests the sum across models and the lock's release. Its production diff changes only a docstring. Round 22 reviewed it and asked for narrower wording here. It also found that startup's overflow rule had no test. Commit 3846666 adds that test and changes no production code. Round 23 agreed at that head, with one wording fix in the draft. CI's test job then failed 22 runner tests on Linux, where the lockfile installs policyengine-us 2.41.0 andpmsetdoes not exist. Fourteen reached the check that the installed engine matches the board's, and eight reachedhost_power. Commit 2ee8acf makes the tests'Worldfixture report policyengine-us 2.60.4 and AC power with the lid open. Commit 0ab2b1e adds 6 tests of the checks that read those facts. Neither changes production code, and the job passes at 0ab2b1e. Round 24 agreed at that head, with four wording fixes. The run waits for the operator to freeze the pre-registration this PR implements and for Max's go.scripts/confirmatory_run.pyruns the v2 calculator-interface study's confirmatory calculator stage as its draft pre-registration specifies, and writes the run records the confirmatory report checks before it scores anything. It runs the treatment harness unchanged:tool_pilot_argon.pyandpolicybenchhere are 6b00a6f's, and the runner refuses to start if they differ.Nothing here runs a model. The confirmatory run waits on the frozen pre-registration and Max's go.
What it does
order/<model>), then its reruns in passes, and only then decides its next household. The six models run at once.Spend. When spend crosses it, no attempt starts, conversations under way endbudget_stop, and every model's log gets aceilingevent. Admission checks the ceiling and takes its time under the ceiling's lock, so it decides no household after the crossing.AttemptDeadline, with the spend so far, and the model's lanes stop with causedeadline. The conversation endsbudget_stopat its next turn; its spend after the abandon, which can come after the hour, still counts toward the ceiling and goes to the run log aslate-costevents. An attempt whose conversation raises keeps the spend it counted.isinstance, explain traces count as calls, and aTimeouton the escalated request after a truncated turn is class model. An operator failure pauses the model's lanes untilconfirmatory_run.py resume, accepted only if written after the pause began. A harness failure stops them, and so does one inconclusive error that ends runs in more than five households. The runner saves replaced attempts, and fsyncs in-flight records, before their reruns.harness,inconclusive,deadline,pool,runner-error,interruptorsampler(ceiling events have causeceiling). An exception in the runner itself stops every model's lanes after settling and writing what had ended. The signal handler only notes the signal, and the main loop stops the lanes.prev, the sha256 of the line before. Every 15 minutes and at the end,anchors.jsonlgets every log's line count and head and every manifest's sha256, and the design'sanchor_commandruns on its own snapshot of the file (so no push sends a line half written) in the background.scripts/confirmatory_anchor.pyis that command: it commits the snapshot to a clone and pushes it without forcing, so a remote outside the run holds each anchor's time. It holds an exclusive lock in the clone and passes the locked descriptor to every Git command it runs, so a helper, hook or remote helper that outlives a push makes later pushes fail as busy, never writing beside them. It refuses a snapshot that does not extend the clone's file byte for byte. The runner kills a push still running after 5 minutes with its process group, and it pushes the final heads up to three times, a minute apart. Each push's anchor, times, return code and outcome go toanchor-pushes.jsonl.late-unresolvedwith the spend it had counted. After its last check the runner ignores signals, checks once more, and anchors again if a first interrupt arrived in between.--workersmust equal the design's engine workers per model.clock()returnstime.time(), never earlier than a time it returned before, and the runner takes each such time under the lock that orders it. The harness's request times stay raw. The report allows them 0.1 s of slack, widened by the largest backward step the host log shows.The no-tools stage
scripts/confirmatory_no_tools.py(047c868) runs the stage before the calculator stage: the three cheap models answer every drawn household without tools, and their answers only flag references for audit. It calls the harness'srun_householddirectly with each model'snoneprofile and a tool function that raises, so the harness's own resume, which folds earlier attempts into their status and totals, is never used.classifyandaction, unchanged, with no host evidence. A provider or none attempt reruns at once; an operator attempt pauses its model until the operator runs the script again; a harness attempt stops its model; a ceiling attempt stops every model. At most two reruns.households/attempts/<household>-<n>.json, and the new final lists every replaced attempt (number, file, sha256, status, class, reason). Since 0ae6df5 the script decodes and hashes each saved record from one read, an archive's link carries the digest of the bytes it decoded or wrote, and before the move it hashes the final again; a final whose bytes differ stops that model with aStateErrornaming the file. The target-exists and digest checks precede theattemptsfolder's creation, so either refusal leaves the history on disk unchanged (c2ca2dc). After the rename the script hashes the moved bytes again; when a write landed between the first hash and the rename, it moves them back and stops that model the same way (274e1c7). It cannot see a write to the same file during either hash or after the second. Later readers verify surviving archive links and refuse mismatches. If no later saved record holds the last archive's link, startup rebuilds it from the bytes then present. The script resumes from these files alone, refusesprior_attempts, numbering gaps, links that disagree with the files and archives no rule reruns, and resumes an archive left without a final as the next attempt. It lists the stage's runs folder and each model's final andattemptsfolders itself (d31c1d7), a model's finals before itsattemptsfolder, as the report lists them (0ae6df5). One of these folders that does not exist, a link to nothing included, holds no records. A folder it cannot list or whose metadata it cannot read, a final orattemptspath that is no folder, and a model entry the runs folder's listing returned whose metadata it cannot read, a link to nothing or an entry removed since the listing included (0ae6df5), each give aStateErrornaming the path, before any provider call, so a failed listing or metadata read cannot hide saved records or their spend. It opens each saved record without blocking, as the report does, refuses a FIFO, a device or another special file at once, naming it, and still follows a link to a regular file (274e1c7). Before any provider call it refuses, with aStateErrornaming the file, a saved final or archive that it cannot read or decode: each must be a JSON object in strict UTF-8, and the script refuses one nested too deeply to decode the same way, and one whosetotalsis no object, or whosetotals.cost_usdis missing,null, a boolean, a string, a list or another non-number, or is negative, non-finite or too large to convert to a float: the report's own rule (8b6c8f7). It also checks each saved record against the pinnednonearm by the report's treatment rule (0300257): model and arm, prompt version, wording and notice hashes, null calculator fields, the board's outputs and each turn's served model, and, for a record with a completed reply or an answer, its contract, sent prompt and prompt hashes, which it rebuilds through the harness. A cheap model's final must name a drawn household and hold a predictions object, and a final's attempt links must number 1, 2 and so on, as integers. The calculator runner's preflight reports each of these as a problem naming the file.baseline_exclusions({path, sha256}): the board's exclusions as they stood before the stage's first call. Before any provider call, the script refuses to start unless that file hashes as declared and has the form the report reads: an object whoseexclusionslist holds one object per cell, with nonemptyscenario_idandvariablestrings and no cell twice ({"exclusions": []}when empty). The script reads the file once, checks the hash of those bytes and decodes the same bytes (d31c1d7), all before it takes the invocation lock or loads a board, and it refuses a baseline file that nests too deeply, is a JSON array or scalar, or holds invalid UTF-8, naming its path. A design the CLI cannot read or decode, or one that is not a JSON object, gives the usual cannot-start message naming the design's path, with exit 1. Before the harness's unchanged loader reads the board, the script checks each board file itself: the metadata sidecar,reference_outputs.csv,reference_exclusions.jsonandscenarios.csvwith each scenario's JSON. A file it cannot read, a CSV that is not strict UTF-8 or that pandas cannot parse, JSON that is not an object in strict UTF-8 or that nests too deeply, and a sidecar whose engine differs from the design's give the same cannot-start message naming that file, with exit 1. A board with no exclusions file has none, as in the harness, and the same refusal covers the check of whether that file exists. The harness's loader then reads the board again (8b6c8f7): an I/O failure there names the file the error names, or the board folder when it names none, and a covered non-I/O failure (aValueError,TypeError,AttributeError,KeyError,RecursionError,OverflowErrororSystemExit) names each board file whose bytes changed after its check, or the board folder when none changed; any other exception can end the script without naming a file. None of these refusals covers running out of memory: an allocation failure while reading, hashing or decoding these files can end the script with an error that need not name the file, always before any provider call, and the script releases the invocation lock first.flockon.confirmatory-no-tools.lockin the stage's runs folder, held for the whole invocation, dry runs included. A second invocation refuses.execute, a thread that fails to start included, sets the interrupt under the admission lock and waits for every started thread before the script restores the handlers and releases the invocation lock. The script tracks each thread's start and finish with its own events, resumes a wait cut short by another exception, and gives a thread whosestart()raised 10 seconds to begin; one that begins later returns before touching a file. It tracks the watcher thread the same way. It restores the handlers before it closes the watcher's pipe. The original exception propagates. An exception caught in a model's thread marks that modelerrorand does not set the interrupt; the other models go on, and the exit is nonzero.--dry-runlists the standing households and the next attempt owed, with no provider call.The calculator runner changes in its
preflight(5a9735b) and, since d31c1d7, in its listings of saved evidence and inmain. It callsstage_problems, which calls no provider. It applies the script's own history and treatment checks, which classify every saved final (since 248e777, an optional model's too) and, since 0300257, refuse a cheap model's final outside the draw. It loads the board, with the script's checks, when the first saved record needs it (0300257), and it refuses to start while any no-tools history fails them or any cheap model lacks a standing final for a drawn household. Since 274e1c7 it also refuses, as the report does, any model's final that the script would rerun, at once or after the operator's pause, and any archived attempts that no final lists. Startup accepts these histories for recovery; execution resumes only the selected models' drawn households. Since 4441364, like startup, the preflight checks the baseline exclusions file, and it refuses aspent_beforethat cannot convert to a finite, nonnegative number or that lies more than the report's $0.000001 under the spend the saved records hold, every model's included. For histories that pass validation, the preflight and the report each sum every saved cost withmath.fsum. Both map anOverflowErrorto infinity and refuse that total (ceae93f). It also refuses no-tools and calculator runs roots that are the same folder or where one contains the other. It reads the eligible pool once, checks the hash of those bytes and decodes the same bytes; a pool it cannot read or decode is a problem naming the pool's path. A board engine it cannot read is a problem namingreference_outputs.csv.meta.json, andmainguards its own engine check the same way, prints every problem under "The run cannot start" and exits 1 before it builds a run or calls a provider. A saved final, run log or host log that the preflight or the summary cannot list or examine gives a problem or refusal naming the path. An entry that its listing of a model's arms or an arm's stages returned, and whose metadata it then cannot read, a link to nothing included, is a problem naming the entry (0ae6df5).Invariants
The tests' oracle (
run_problemsintests/test_confirmatory_run.py) checks these on every fake run, and the property test checks them on random mixes of planted failures:clock()never returns a time earlier than one it has returned, including under a wall clock that steps back (test_the_clock_never_goes_backandtest_a_wall_clock_that_steps_back_keeps_the_run_procedure). The runner writes an attempt's line when its household ends, and aceilingevent carries the crossing's time, so timestamps can appear out of order in the run log. Every check that orders events reads their times, never their line order.late-costevent.For histories that pass validation, the preflight and the report sum the flat list of saved costs with
math.fsum, so their total holds for any order of those costs.test_the_exact_sum_ignores_orderand the preflight's permutation test check that total. The differential requires both programs to reach one spend decision for generated costs in three cheap finals, an archive and an optional final. It also checks the listed boundaries, declaration forms and baseline files.Tests
tests/test_confirmatory_no_tools.py(205 tests) andtests/test_confirmatory_no_tools_records.py(200), fake household implementations, plus dedicated conversations through the real harness: draw order and one thread per model; a rerun links the unchanged archived bytes; a third provider failure stands with two archives; operator pause and resume; harness and budget stops that persist; a crash after the archive move; spend carried across invocations and optional models; refusals of foreign, gapped and corrupted histories; an attempt that raises keeps its spend; the cap agrees with the report's. Commit 08d7b18 adds a realos.kill(os.getpid(), signal.SIGINT)from inside a fake provider call (the attempt saves, no later household starts, the exit is 1, the script restores the handlers), a secondStagerefused while the first holds the lock, both baseline refusals, archive identity and rerun eligibility, a harness stop at attempt 3, and a conversation through the realrun_householdthat ends at its next turn after another model's spend crosses the cap. c60086b to 4ce7f8e add real signals between the interrupt check and an archive or an attempt (both refused); a second thread that fails to start while the first holds a fake provider (an independentflockprobe shows the invocation lock held until the first finishes); a second signal while a worker holds the output lock (exit 128 plus SIGTERM within the deadline); optional and off-draw finals that the script cannot classify, refused at startup and by the preflight; and baseline files that are a hashed bare list, invalid JSON or missing a field. Commit 2f795ea replaces the handler's lock with the watcher and adds tests that run each signal path in a bounded subprocess: a real SIGINT inside the cleanup's own Event condition (the worker saves, the handlers stay installed and the invocation lock held until it does); exceptions injected once and twice into the cleanup's waits; astart()that raises before or after the native thread exists, within and past the start grace; a signal while a model's thread holds its admission (with the lock removed from the worker or from the watcher, all four fail); a second signal while the watcher waits for admission or while a worker holds the output lock; and a signal after every model finished (exit 1). The tests that slept 50 ms now wait for the interrupt event, and the forced-exit deadline is 30 s. Commit 9acc920 fixes a race in one new probe: its control thread could exit the process before the main thread's handler did (at 6597adb it failed that way in two of three runs: once in run 2, twice in run 3). The two forced-exit probes then pass five more runs in a row. a1114fe adds six cases in bounded subprocesses: the script must restore the handlers before either of the watcher's pipe descriptors closes, and a real SIGINT after a new file takes a descriptor's number must write nothing to its new owner (with the teardown order reversed, both fail); a watcher whosestart()raises before or after the native thread exists, or whose target begins after the start grace (the original error propagates, the script releases the handlers and descriptors, and a late target returns without reading a closed descriptor); and a first SIGINT after every model finished (exit 1, every final saved, no interrupt set and no announcement). All six pass on the script at 9acc920, which needed no change. Commit 89dd946 qualifies the docstrings and comments on the handler and the cleanup's waits; its executable code is identical to 9acc920's. Commit 4dafb83 adds 12 cases: a saved final, an archive, a baseline file and a design that each nest too deeply, hold a JSON array or hold an invalid UTF-8 byte inside a string, and the script must refuse each, naming its path. Each case runs in a bounded subprocess. At 89dd946 all four nested cases escaped asRecursionError, and the baseline and design refusals did not name their files (8 failed, 4 passedwith the new tests on the old script). Commits 040d206 and 6928b06 add 16 cases, twelve on a small real board and four on saved costs: the script must refuse a metadata sidecar, an exclusions file and a scenario's JSON that each nest too deeply, hold a JSON array or hold an invalid UTF-8 byte, and a reference CSV with an invalid byte, naming the file each time (10 cases); it must refuse an oversized saved cost in a final and in an archive, naming the file, at startup and in the preflight (4); and a valid board, with an empty exclusions file and with none, must load through the harness exactly as the harness loads it (2). At 4dafb83 the 14 fault cases fail and the two valid boards pass. The CLI test now runs on a real board. Commits 8b6c8f7 and 0300257 add 310 cases. In the first file, the harness's reread fails with an I/O error, or meets a sidecar or a scenario's JSON rewritten after its check to JSON nested too deeply, and the exclusions file's existence check fails; each must stop the script naming the file (or the board folder, when the error names no file), with no provider called. Saved costs oftrue,"0.25",null,[]and a missing cost, in a final and in an archive, the script must refuse each, naming the file, at startup and in the preflight. The new second file builds saved records with the harness on a real board and changes one treatment field, attempt link or prediction at a time: a change the report refuses, the script must refuse at startup and in the preflight, and a change the report accepts, the script must accept. At 6928b06, 229 of the new cases fail and 81 controls pass. Commits 5e334d9 to 4d93b40 add 40 cases in three new files.tests/test_confirmatory_unreadable_evidence.pymakes listings of saved evidence fail (the no-tools runs folder, a model's final andattemptsfolders, and the calculator runner's model, arm, stage and households folders) and metadata reads fail (those folders, a listed model entry, the run and host logs, an archive's target and the board's exclusions file), at the script's startup, in the calculator runner's summary and through the full preflight, and requires a refusal or problem naming the folder or file, with no provider called.tests/test_confirmatory_input_reads.pyreplaces the baseline exclusions file's and the eligible pool's bytes between the hash and a second read, with invalid JSON and with an invalid UTF-8 byte, and requires the refusal; it also saves a record whosevariablesis an object, which the report accepts, as a final and as an archive, and requires the script to accept both.tests/test_confirmatory_board_engine.pyruns the realboard_engineagainst a malformed and an unreadable sidecar through the preflight and throughmain, and requires the sidecar's name, no provider call and, formain, exit 1 with "The run cannot start". At 4fb80d9, 38 of the new cases fail and the twovariablescontrols pass. Commit 0ae6df5 adds 21 cases to these files. In the first: a model entry that is a link to nothing or that vanishes after the listing, at startup and in the preflight; a calculator arm or stage that does the same, in the preflight; and a file at, or a failed metadata read on, an optional model's final orattemptsfolder, at startup and in the preflight. Each requires a refusal or problem naming the entry, with no provider called. In the second: an archive and a final that the script must read once, hashing the bytes it decoded; an archive link whose digest matches the bytes the script read for its final, a control; and a final changed after the script read it, and after it wrote it, which must stop the model before the move.tests/test_confirmatory_board_engine.pynow requires the preflight guard's own line, once, and each cheap model's saved-treatment line beside it. Run against the script at 4d93b40, 12 of the new and changed cases from 0ae6df5 and c2ca2dc fail and 13 pass. The tests in 274e1c7 and 0193811 require the preflight to refuse, and startup to resume, an unfinished optional history and archived attempts outside the draw. They refuse a FIFO and a link to a device as a final and as an archive within 5 seconds, at startup and in the preflight. They replace a final with a FIFO before its move, and they put an archive at the target's name and require both files to stay. A refused move must leave noattemptsfolder, and the script must move back a final changed just before its rename. Run against the script at c2ca2dc, 15 of those cases fail and 7 pass, and a test catches each of 10 mutants of the fixes. The tests in 4441364 put saved spend in a final, an archive and an optional model's final on both sides of the tolerance. They refuse aspent_beforethat is no number, infinite, null, nonnumeric text, negative or too large to convert, and they plant each baseline fault at the preflight. They replace an archive target with a FIFO after its rename. When a second read or a move back fails during execution, the other models must finish, the lock must come free and startup must recover the archive. The deadline test now records the join's timeout, and the ceiling test waits for the crossing. Withnt.MONEY_TOLERANCEwritten as1e-6in the probe's test copy, 15 of the 24 selected cases failed at a8226dd, and a test caught each of 21 mutants. The tests in ceae93f permute saved costs at the tolerance, preserve costs a running total loses and refuse an overflowing total. They also check numeric-text and boolean declarations and refuse a design withoutspent_before. Against the script at 4441364, 7 of the 17 cases selected failed, and a test caught each of 24 mutants. The test in 3c80377 saves costs whose running total stays under the stage cap and whose exact sum reaches it, and requires that no owed attempt starts and that the lock comes free. A test caught each of 25 mutants. The new test in 58ba64b saves costs whose subtotals per model fall one float under the cap and whose exact sum reaches it, and both tests take the invocation lock from a second open. A test caught each of 27 mutants. The new test in 3846666 saves two valid finals of 1.5e308 and requires that startup takes infinity for the total, starts no attempt and frees the lock. A test caught each of 28 mutants. Commit 0ab2b1e adds 6 tests: the preflight andmainmust refuse an installed engine other than the board's, andhost_powermust read the four mixes of power source and lid state. A test catches each of 33 mutants. With the existing files and the three newer ones, 778 pass at 0ab2b1e, one worker, in each of three runs in a row, with the report at 02d25d0 (772 at 3846666, 771 at 58ba64b, 770 at 3c80377, 769 at ceae93f, 752 at 4441364, 730 at a8226dd, 714 at c2ca2dc, 693 at 4d93b40, 653 at 4fb80d9, 343 at 6928b06, 327 at 4dafb83, 315 at 89dd946, 309 at 9acc920, 297 at 4ce7f8e). The two forced-exit probes pass five more runs. For this branch, CI requires a pull request intomain, so temporary draft CI run for #244 (closes when the run ends) #248 carries this head to it. Its test job runstests/on Linux with the lockfile's engine, except the tests marked slow, and none of this PR's tests carries that mark. At 0ab2b1e the job reports 6,675 passed, 92 skipped and 47 deselected (Actions run 41179707195). CI has no report checkout, so the acceptance controls that needCONFIRMATORY_REPORTskip there and run in the three runs above. This PR changes onlyscripts/confirmatory_*.py,tests/test_confirmatory_*.pyandtests/fake_anchor.py. Commit 060b287 makes two host-log tests (test_the_host_log_samples_and_closesandtest_pressure_near_an_attempt_is_host_trouble_once_settled) wait for the samples they need instead of sleeping a fixed time; on a loaded host the 0.02 s sampler had missed both windows.tests/test_confirmatory_run.pyandtests/test_confirmatory_anchor.py: 248 pass at 4002f8e, one worker. The anchor tests run the helper against a real temporary clone and a local bare remote. They kill the helper inside Git's pre-commit, pre-push and pre-receive hooks, inside a clean filter duringgit addand duringgit status, and with a detached descendant holding the lock. Each checks that later helpers fail as busy and leave the clone's file, index, HEAD and the remote unchanged, and the clean-filter and descendant tests each fail when a fault removes inheritance from only the command they target. Tests compare fake OpenAI Responses and Gemini JSON conversations with the unchanged treatment harness's, complete requests and calculator inputs included.v2-microcosm-batchesget no CI, sinceci.ymlruns on pull requests to main.clock()it fails every time: attempts overlap, and a household runs before its admission.test_confirmatory_differential.py). It runs this runner'sRunon a copy of the development board. The harness'srun_householdis real, and so are this runner's host log and lanes. Only three things are fakes: the model (replying in each API's shape: chat tool, chat JSON, Responses), the engine pool and the host's readers. The confirmatory report'sanalysethen checks every record and log. At the report's 7cf22e2 against 4002f8e, 27 tests pass, and a 60-example run of its property test passed. At the report's ef297fa against 047c868, 34 pass, among them the no-tools script's own output checked by the report, and a 60-example run of the property test passed. At the report's 0b8e275 against 5a9735b, 39 pass, among them a harness stop and a ceiling stop on the last household, each of which the report refuses, and a 60-example run of the property test passed. At the report's 35005db against 89dd946, 64 pass, among them 15 baseline files that this runner and the report must both accept or both refuse, and a 60-example run of the property test passed. At the report's 31a2964 against 6928b06, 66 pass, among them a baseline file with an invalid byte inside it, which both programs must refuse; its baseline file meant to fail decoding by its depth decoded on the pinned Python (round 14). At the report's 563ef03 against 4fb80d9, 76 passed, among them a baseline file nested 20,000 deep, which both programs must refuse with the decoder's recursion error as the cause, and saved costs oftrue,"0.25",null,[]and a missing cost, in a final and in an archive, which both must refuse before any provider call, and a 60-example run of the property test passed. At the report's 8df8531 against 4d93b40, 78 passed, among them a no-tools final folder and anattemptsfolder that neither program can list, which both must refuse before any provider call, each naming the folder, and a 60-example run of the property test passed. At the report's 61e5eef against c2ca2dc, 79 pass, among them a model entry that is a link to nothing, which both programs must refuse before any provider call, naming the entry, and the unlistable final folder, which each must name as itself, and a 60-example run of the property test passed. At the report's 84a9f63 against a8226dd, 84 pass, among them a final and an archive that are FIFOs, which both programs must refuse without blocking, and an optional model's history left by a pause, an interrupt or a crash, which both must refuse, and a 60-example run of the property test passed. At the report's 58e4533 against 4441364, 90 passed, among them baselines and saved spend on which the preflight and the report must give one answer, in listed cases and in a property over saved costs, and a 60-example run oftest_any_runner_run_passes_the_reportpassed. At the report's b52164b against ceae93f, 125 passed, among them saved finals, archives and optional finals at five floats around the spend tolerance. A 60-example run oftest_any_runner_run_passes_the_reportand oftest_any_saved_spend_gets_one_answer_from_both_programspassed. At the report's 1476850 against 3c80377, 128 passed. Both programs must refuse a saved total that overflows and accept a declaration of the largest float when the total rounds to it. A 60-example run of both properties passed. At the report's a816d91 against 58ba64b, 128 passed, and a 60-example run of both properties passed. At the report's 02d25d0 against 3846666, 128 passed, and a 60-example run of both properties passed. At the report's 02d25d0 against 0ab2b1e, 128 pass, and a 60-example run of both properties passed. (At eda54e1 against f5099db, 22 passed, among them the same jittered clock.)Plan
spent_before(P2) and never read the baseline (P2), several faults passed the tests (P3), and this description and the draft overstated later digest checks (P3); answered in 4441364 and here🤖 Generated with Claude Code