From 1db370d5f6bdf6bb3a49c9caa0d8cc3724e5e871 Mon Sep 17 00:00:00 2001 From: Tim Stranske Date: Sat, 29 Aug 2026 13:31:11 -0500 Subject: [PATCH 1/2] feat(testgen): rank test-writing work by where testing actually FAILED, not by uncovered lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEDUP (CLAUDE.md §0). Checked src/ for priorit/rank/hotspot/target — only capability_targets.py, which ranks CAPABILITIES not test targets. Grepped the tree for hotspot/prioriti/rank_. Searched the improvement log for "escaped defect" and "test priority": both reported a real absence with the log read in full. Nearest existing thing is coverage_trend.compute_top_files, which ranks by LOWEST COVERAGE — precisely the ordering this exists not to lead with. Not present; building new. `capability_admission.py --preflight` clears with zero blocking failures; caller, heartbeat and fixture remain obligations, and the fixture is discharged here. WHY NOT MOST-UNCOVERED-FIRST. That maximises percentage-per-PR and points agents at the largest uncovered files, which are the glue modules where a meaningful test is hardest to write. It is the ordering most likely to produce hollow tests, so uncovered mass is the LAST tier, not the first. THREE TIERS, lexicographic: 1. ESCAPED DEFECTS — a file that later needed a fix is a file whose tests missed something. The only tier reporting observed failure of the TESTS rather than a property of the code. 2. CHURN — where regressions actually arrive. 3. UNCOVERED MASS — how far the metric moves. Last, deliberately. All three multiplied by (1 - hollow_rate), so a module where agents keep producing tests that pass against a broken base sinks however much uncovered code it has. That term only became measurable when testgen_gate grew no_hollow_nodes in #131. LEXICOGRAPHIC, NOT A WEIGHTED SUM, and the first version was the latter. Multiplying the tiers apart (1e6/1e3/1) and summing holds only while the lower tiers stay small: at 1,000,000 uncovered statements tier 3 exactly equals one escaped defect and the ordering inverts. A scoring function whose correctness depends on inputs staying under a magic threshold is a defect waiting for a big repository. Two of the new tests caught it during review; the tuple holds at any magnitude and the tests now assert at 10**9. No blended "score" is reported at all — one number formed from three incomparable tiers invites exactly the trade-off the tuple forbids. TIER 1 IS A GIT PROXY TODAY AND SAYS SO. The Brain has the better signal in outcomes.durability, where broke_later means merged, CI green, broke afterwards. Measured across 4,665 outcome rows: durable 2842, abandoned 1300, pending 517, reverted 4, reworked 2, broke_later ZERO — durability_sweep assigns "reopened, reverted, or durable" and never broke_later, while pattern_miner.TERMINAL_FAILURE_DURABILITY consumes it. A consumer for a label nothing produces. Six escaped-defect rows cannot order a queue, so tier 1 reads git history, which needs no instrumentation and exists in every repo. brain_signal_status() reports which source is live and returns "unknown" rather than zero when the store cannot be read, so the day the Brain signal becomes usable is visible rather than assumed. RANKING IS NOT TRAINING. A fix commit touching a file is decent evidence for ORDERING work and poor evidence for a learner: code changes for many reasons. This module ranks; it never writes durability labels. A broke_later producer must clear a higher bar and is deliberately separate. A SECOND DEFECT OF MY OWN, caught while validating on real data: the CLI treated a missing --coverage-json as an empty report, so tier 3 rendered as a column of zeros that reads as "everything is covered" rather than "nothing was read" — the same could-not-measure-as-measured- zero shape this module's own notes describe. It now names the file and the failure. Found when a scratch path was cleaned up between runs and the ranking carried cheerfully on. VALIDATED ON REAL DATA. stranske/Trend_Model_Project, its real Gate coverage payload and its real history: multi_period/engine.py ranks first on 9 fix commits, 28 churn, 335 uncovered — while 3_Results.py, which has the MOST uncovered statements at 759, ranks third. That inversion is the design working. Break -> revert on both invariants: restoring the weighted sum fails the two tier-ordering tests; removing the hollow discount fails the two hollow tests. Byte-identical revert, 13 pass. Tests are pytest, not selftest cases, on purpose: local_verify grades per pytest NODE, so a selftest is one node and its assertions are invisible to hollow detection. A module that orders test-writing work should have its own tests gradeable by the gate judging that work. The module keeps a --selftest as well (88/88), exercising the CLI as it ships including live git parsing. Verified: 515 passed, 0 failed, 0 skipped, 88/88 selftests, 5/5 gates. Floor 502 -> 515. Co-Authored-By: Claude Opus 5 --- .verify-floor.json | 2 +- src/escaped_defect_priority.py | 441 ++++++++++++++++++++++++++ tests/test_escaped_defect_priority.py | 191 +++++++++++ 3 files changed, 633 insertions(+), 1 deletion(-) create mode 100644 src/escaped_defect_priority.py create mode 100644 tests/test_escaped_defect_priority.py diff --git a/.verify-floor.json b/.verify-floor.json index 7934b95..f51d834 100644 --- a/.verify-floor.json +++ b/.verify-floor.json @@ -6,5 +6,5 @@ "selftest_skipped_max": 7, "gate_skipped_max": 2, "mypy_exempt_max": 0, - "note": "Recorded by verify.py --update-floor, except the *_max ceilings, which are edited BY HAND and never re-measured. `collected` catches tests that stopped being collected; `passed` is compared against passed+skipped, so a check may move between passing and consciously-skipped but the two together may never shrink. The *_max ceilings bound the skipped side: 24/7/2 is exactly what a machine with none of this instance's local prerequisites skips (a GitHub runner: no agent CLIs, no ~/.codex/skills, no /Applications/ChatGPT.app, no populated capability ledger), measured 2026-08-21. On the owner's machine all prerequisites exist and nothing skips at all. Raising a ceiling is a deliberate act: it means agreeing that one more thing is allowed to go unchecked, so say which and why in the commit. LOWERED 26 -> 24 on 2026-08-22, reverting the raise made earlier the same day. The two kill-switch exemption tests no longer need to skip on a bare runner: their declarations moved out of the running instance's ledger and into capabilities.KNOWN_DECLARATIONS, so they assert code-derived truth and run everywhere. Moving a test back below the ceiling is the preferred way to lower it -- fix what made it machine-dependent, rather than agreeing to check less. FLOOR 345 -> 353 on 2026-08-22: 345 was measured on a branch cut before #13 (research panels/rounds/domain studies) merged, so the recorded floor sat 8 tests BELOW what main actually collects. A floor below reality is the permissive direction -- those 8 could have silently stopped being collected and still cleared the check, which is exactly the hole this file exists to close. Measure the floor on the merge result, not on the branch. Raised again on 2026-08-22 by the producer-identity-scope branch, which adds tests on top of the 353 recorded by #15; re-measured after rebasing rather than assumed. NOTE: `verify.py --update-floor` REPLACES this note with a generic one, so it must be restored by hand after every use \u2014 the ceiling rationale is the only record of which prerequisite justifies each skip. FLOOR 365 -> 366 on 2026-08-22 (heartbeat-ordering work, PR #18): exactly one new test, test_capabilities.test_no_tick_producer_runs_above_the_heartbeat_export. No ceiling moved and nothing new is skipped -- it reads source files rather than a populated ledger, so it runs on any machine. The branch recorded 354 because it was cut before #16 merged; re-measured on the MERGE RESULT per the rule above, which is exactly the mistake that put the floor 8 below reality last time. FLOOR 366 -> 368 on 2026-08-23: main collected 368 while this file recorded 366, drift left by #34 (evidence-acquisition landed, +1) and #37 (tick capability evidence, +1) whose authors each measured against a branch cut before the other merged. A floor BELOW reality is the permissive direction this file exists to close -- those two could have silently stopped being collected and still cleared the check. Measured on the merge result per the rule above: 368 passed, 0 failed, 0 skipped, 83/83 selftests, 43/43 can-fire, 5/5 gates. CEILING 24 -> 26 and FLOOR 368 -> 387 on 2026-08-23 (profiles/provenance branch, PR #42). This file CONFLICTED with #50, which raised the floor 366 -> 368 on main while this branch raised it to 387; resolved as the UNION rather than by taking a side -- #50's rationale is retained above and the count was RE-MEASURED on the new merge result instead of keeping either number. 368 (main) + 19 (this branch's net new tests) = 387; #50 corrected recorded drift rather than adding coverage, which is why 387 is unchanged from the pre-conflict measurement. Measured in a runner sandbox reproducing CI exactly (361 passed, 26 skipped, 387 collected) AND on the owner's machine (387 passed, 0 skipped, 5/5 gates). The two new skips are drift detectors against a REAL installed agent runtime, so neither can be moved below the ceiling -- the preferred way to lower one: (1) agy advertised-models cache absent, since comparing declared model ids against the catalogue agy actually advertises needs that catalogue, and a fixture would exercise the comparison while detecting no real drift; (2) vibe config absent (~/.vibe/config.toml), since active_model cannot be read to check for drift when there is no config to read. Both name their missing prerequisite, so a green run still states what it did not check. A third candidate skip was REFUSED: dispatcher's per-run agy-log assertion failed on a bare runner because adapters.advertised_models shells out to `agy models` when its disk cache is cold, and that probe landed inside a monkeypatched subprocess.run and overwrote the captured command. That is a stub leak, so it was fixed by ISOLATING the double rather than by skipping -- which makes CI run MORE. FLOOR 387 -> 391 on 2026-08-23 (improvement-log accessor, PR #59): exactly four new tests, all in test_improvement_log.py -- three read tracked files in the tree (the pointer's size and content, and that CLAUDE.md 0 step 3 and 5 name the accessor rather than a bare path) and one runs the accessor as a subprocess against a path that cannot exist. None reads a populated ledger, an agent CLI or ~/.codex, so all four RUN on a bare runner and NO ceiling moved: nothing new is skipped. Measured on the MERGE RESULT after rebasing onto origin/main af6654d, which collected 387 -- not on the branch base, per the rule above. FLOOR 391 -> 402 on 2026-08-23 (Gate python-ci configuration, the PR that adds the missing .github/workflows/autofix-versions.env): exactly 11 new tests, all in test_ci_gate_config.py, which read committed files only -- the pin file, ruff.toml, mypy.ini, pr-00-gate.yml's toggle annotations and docs/CI_LINT_BASELINE.md. NO ceiling moved. On any CHECKOUT -- CI, the owner's tree, a second instance -- all 11 run: they need no installed linter and no populated ledger. In the EXEC-MIRROR layout all 11 skip with one named reason, because orch-sync-mirror.sh copies root-level *.py only, so .github/workflows, docs/ and scripts/ are genuinely absent there (env_prereq.repo_files_absent). That lands at 11/26 on a machine that otherwise skips nothing, and CI stays at 26/26, so no ceiling needed raising. The skip gate is the presence of those DIRECTORIES, never of the pin file itself -- gating on the file would have made the test that checks for it unable to fail. Measured on the merge result, twice: the branch was rebuilt on origin/main after #42 and #59 merged, and re-measured after #61 merged and was merged in -- 393 passed + 9 skipped = 402 collected both times, so #61 added no collected tests and this floor is not sitting below reality. #61 itself left main's floor at 391, which is exactly main-without-these-11, so there is no inherited drift to correct. RULE CHANGE 2026-08-23: `collected` is now an EQUALITY, not a minimum. Every floor entry above this one records the number being found BELOW reality and hand-raised after the fact -- 21 low at the worst, then 8, then 1, then 2 -- because nothing ever required a test-adding PR to touch this file, so the permissive direction was silent by construction and the rule 'measure on the merge result' had to be restated three times with nothing enforcing it. verify.py now FAILS when collected exceeds the floor, printing the two integers to write. That also makes the concurrency case self-enforcing: once every test-adding branch must edit these same two lines, two concurrent branches CONFLICT IN GIT, so the second cannot merge without rebasing onto the first and re-measuring on the actual merge result. Demonstrated repeatedly on the change itself: six merges landed on main in the two hours it took to write, moving this file 368 -> 387 -> 391 -> 402, and every one would have left the floor below reality under the old one-directional rule. `passed` deliberately stays a MINIMUM on passed+skipped: only collection is machine-invariant (a skipped test is still collected), measured across machines at 391 collected on both, with pass/skip splits of 365/26 on CI against 391/0 locally. The *_max ceilings are untouched by this change and nothing new is skipped. `--update-floor` also stops REPLACING this note -- it appends -- so the warning above about restoring it by hand no longer applies; and drift does NOT block --update-floor, since a gate that forbade its own only remedy would be a deadlock (the first draft was exactly that). FLOOR 402 -> 407 on 2026-08-23 (findability admission requirement). (findability admission requirement). (findability admission requirement). (findability admission requirement). Exactly five new pytest tests, all in test_capability_admission.py: test_findability_distinguishes_its_three_sub_causes, test_findability_blocks_new_capabilities_and_reports_older_ones_as_debt, test_unreadable_reach_is_not_evaluated_and_never_a_failure, test_findability_exemption_is_declared_in_code_not_in_a_live_ledger, test_consult_sites_are_falsifiable_claims_about_real_callers. NO CEILING MOVED and nothing new skips: all five build synthetic ledgers in a tempdir or read committed tables, so none needs a populated capability ledger, an agent CLI or ~/.claude/skills. The one machine-dependent thing they touch -- an external consult site declared in capability_advisor.CONSULT_SITES whose skill prompt is not on this machine -- is reported as UNVERIFIED rather than skipped, because absence of the caller is not refutation of the claim; the in-tree site (tick) is asserted verified on every machine so the check can never degrade into 'everything unverified, nothing tested'. Measured on the merge result per the rule above: this file CONFLICTED three times while the branch was open, as main went 387 -> 391 -> 402 (#61, #64, #65, #60). Each time it was resolved as the UNION rather than by taking a side, and the count was RE-MEASURED on the new merge result rather than either number being carried forward: 402 (main at bd6da2e) + 5 (this branch's new tests) = 407. That is the rule this file already states -- measure the floor on the merge result, not on the branch -- and it mattered here, because #60 both deleted test_ci_gate_config.py and added more than it removed, so guessing in either direction would have been wrong. -> re-measured on 2026-08-23 (PR #62, the four deferred #42 review findings): three new tests, all machine-independent (each builds its own tmp_path Brain and manifests), so NO CEILING MOVED and nothing new is skipped. Fourth conflict for this branch, and the first one under the EQUALITY -- which is the point: the equality's own rationale says git conflict detection is what enforces 'measure on the merge result', and that is exactly what happened here. Under the old minimum the three earlier conflicts could each have been resolved by keeping the larger number; under the equality the count MUST be measured, and it was. RESOLVED AGAINST #68 (findability admission requirement) on 2026-08-23: taken as the UNION per the rule this file states -- #68's five-test entry is retained above and this branch's three-test entry beside it -- and the count RE-MEASURED on the merge result rather than keeping either side's number. main fc1fd42 collects 407; this branch adds 3; 410 measured with `pytest --collect-only -q` on the merge result, not assumed. Ceilings untouched at 26/7/2 and nothing new is skipped. Also resolved in the same merge: langsmith-fleet-worker-attempt.json, a CI-emitted `langsmith-fleet/v1` worker-attempt record whose two sides differed only in `emitted_at` and `pr_number` (62 here, 68 on main). Main's NEWER record was kept rather than this branch's older one -- discarding a newer provenance observation to win a merge would corrupt exactly the causal-provenance evidence CLAUDE.md 2 protects, and this branch's own run re-emits its record anyway. FLOOR 410 -> 411 on 2026-08-23 (CodeRabbit follow-up on PR #42, thread 3837879039; re-measured again after #56 made `collected` an EQUALITY, which makes an assumed number a hard RED rather than a quiet pass -- main stayed at 402 across #56, and the merge result measures 403, so #56 added no collected tests and this is main's 402 plus this branch's one): exactly one new test, test_feedback_model_provenance.test_late_sweep_completes_terminal_attempts_never_one_in_flight, which pins that ledger_reconcile.resolve_unresolved_worker_attempts completes only TERMINAL unresolved worker attempts and never one still in flight. No ceiling moved and nothing new is skipped -- the test builds its own tmp ledger and codex rollout fixture and monkeypatches adapters.CODEX_SESSIONS, so it needs no agent CLI and no populated capability ledger and runs on a bare runner. RESOLVED AGAINST #59 (improvement-log accessor), which raised the floor 387 -> 391 on main while this branch raised it to 388: taken as the UNION -- #59's rationale is retained above and the count was RE-MEASURED on the new merge result rather than keeping either number, which is the rule this file states and the mistake that once put the floor 8 below reality. 391 (main, incl. #59's four tests) + 1 (this branch's one new test) = 392 measured, not assumed: 392 passed, 0 failed, 0 skipped, 83/83 selftests, 43/43 can-fire, 5/5 gates. Three sibling follow-up branches are in flight against this same main (CI/ruff config, arm-attribution + durability, adapters label->ID); if this file conflicts with one of them, resolve as the UNION and RE-MEASURE on the new merge result rather than taking either number -- that is what #42 and #50 did, and taking a side is what put the floor 8 below reality earlier. RESOLVED AGAINST #68 (findability admission requirement) on 2026-08-23: taken as the UNION per the rule this file states -- #68's five-test entry is retained above and this branch's one-test entry beside it -- and the count RE-MEASURED on the merge result. main fc1fd42 collects 407; this branch adds 1; 408 measured with `pytest --collect-only -q` on the merge result, not assumed. Ceilings untouched at 26/7/2 and nothing new is skipped -- the one new test builds its own tmp ledger and codex rollout fixture, so it runs on a bare runner. Also resolved in the same merge: langsmith-fleet-worker-attempt.json, a CI-emitted `langsmith-fleet/v1` worker-attempt record differing only in `emitted_at` and `pr_number`; main's NEWER record was kept, since discarding a newer provenance observation to win a merge would corrupt the causal-provenance evidence CLAUDE.md 2 protects. FLOOR 411 -> 415 on 2026-08-23 (PR #70 diagnostics salvage): four new collected tests in test_capability_set_coverage.py from the PR #43 salvage plus CodeRabbit follow-ups on PR #51/#70 \u2014 union/missing-candidate fetch command, truncation after six modules, AST-scoped gate-call audit, and entrypoint-diagnosis coverage. NO CEILING MOVED and nothing new is skipped; all inject synthetic ledgers or read committed source. Measured on the merge result at 91d37fa: 389 passed + 26 skipped = 415 collected on CI, not assumed. FLOOR 415 -> 416 on 2026-08-23 (the dangling-citation follow-up, PR #74): exactly ONE new test, test_ci_gate_config.test_every_cited_repo_path_resolves, which reads the two committed config files this repo OWNS (the pin file and ruff.toml) and asserts every repo-relative path they cite exists. It exists because the pin file shipped citing docs/ci/LINT_BASELINE.md when the real path was docs/CI_LINT_BASELINE.md: the sibling checks read that file's CONTENTS thoroughly and its PROSE not at all, and the prose is the only pointer telling a reader where to re-measure before bumping a pin. Scoped to the two owned files deliberately -- scanning pr-00-gate.yml yields six findings that are all correct as written (guarded by hashFiles or a .agents check, or upstream paths), and a test that cries wolf gets waived. NO ceiling moved. RE-MEASURED SIX TIMES as the base moved under this ONE-LINE change: bd6da2e 402 -> ddb0928 402 -> fc1fd42 407 -> 0d661e3 407 -> 0593eeb 411 -> 6fed4ad 415, each +1 with this test, and the branch was rebuilt on each rather than the number carried forward. THIS BRANCH IS THE WORKED EXAMPLE of the equality's concurrency cost, so record it rather than rediscover it: main moved EIGHT times in the ~2.5 hours a one-line comment fix was open (#56, #68, #73, #69, #62, #70 and two direct commits), the floor line conflicted THREE separate times, and two merges overlapped the change directly -- #73 landed a byte-identical copy of the backplane-conformance.yml guard this branch also carried (dropped as redundant), and #69 edited this very test file in a neighbouring region. The equality is still the right call and should stay: every entry above this one records the floor being found BELOW reality, which is the permissive direction. But no amount of author care wins this race, because the correct value is only knowable on the merge result. The durable fix is CI running `verify.py --update-floor` on the merge commit, which keeps the equality and removes the race; until then a test-adding PR must be merged promptly after going green, because it re-conflicts on roughly every subsequent merge. FLOOR 416 -> 427 on 2026-08-23 (PR #72 hygiene untrack, rebased after #71 merged): exactly 11 new tests from test_repo_artifact_hygiene.py with root-anchored gitignore patterns. NO ceiling moved. Measured on merge result after #71 landed on main: 416 (main) + 11 = 427 collected via pytest --collect-only -q, not assumed. #71's simpler untrack landed first; this branch carries the full hygiene test suite and corrected root-anchored patterns. FLOOR 427 -> 428 on 2026-08-23 (PR salvaging #34/#42 remnants): exactly one new test, test_feedback_model_provenance.test_gemini_provenance_reads_the_per_run_log_before_the_conversation_store, recovered from #42's post-merge commit 4e0d6ae along with the adapters catalog work it exercises. No ceiling moved and nothing new is skipped -- it seeds adapters._ADVERTISED_MEMO instead of letting the catalog probe shell out, so it runs on any machine and adds no prerequisite. `passed` is 428 rather than the 426 verify.py suggested on this machine: two test_capabilities liveness tests (test_gate_blocks_execution_is_opt_in_and_narrow, test_evidence_gate_kind_is_not_blanket_observer) currently fail HERE on pristine main as well, because the hourly fleet tick mutated the machine-local ledger and range-lane-rollout now classifies matched_not_invoked rather than deliberately_gated. That is ledger STATE, not this branch and not the code -- CI bootstraps an empty ledger and counts 428/428. Recording 426 would have baked a local environment failure into the floor as though it were the expected result. FLOOR 428 -> 441 on 2026-08-23 (coverage measures what actually runs): exactly 12 new tests, all in test_verify_coverage_mode.py. They read committed files and verify.py's own source, and monkeypatch verify.COVERAGE in-process -- no populated ledger, no agent CLI, no ~/.codex, and no coverage RUN -- so all 12 execute on any machine and NO ceiling moved. The change itself is a measurement fix, not a gate: `verify.py --coverage` wraps each child in `coverage run --parallel-mode` and combines, because the per-module --selftest is a SUBPROCESS and a pytest-only coverage run cannot see it. That blind spot was most of the codebase -- 78 modules have no test_*.py at all, ~85,500 lines, 79.6% of non-test root Python -- so the reported 48.45% was measuring the gap in the instrument, not a gap in the tests. Combined: 76.1% (45,049 statements, 10,774 missed). Twelve of the twelve modules the old report named as worst were selftest-only; outcomes.py reported 9.0% and measures 61.9%, watch.py 9.6% -> 86.9%. Coverage is OFF by default and deliberately never touches the exit code -- one of the 12 tests pins that, because enforcing a threshold here would reward pytest wrappers around already-tested modules: metric up, assurance flat. Measured on the MERGE RESULT: branched from origin/main bcc68cd (floor 427), then REBASED onto f5f1c39 when it landed underneath and re-measured on the new merge result rather than carrying the old number: 428 + 13 = 441. +1 on 2026-08-23 (440 -> 441 after the rebase), same branch: test_the_cli_help_actually_renders. It exists because this branch BROKE `verify.py --help` and its own twelve tests did not notice. argparse interpolates help strings with `% params`, so the literal `~80%` in the --coverage help was read as an `%o` octal conversion and --help died with 'badly formed help string'. All twelve original tests passed: every one inspected source text or monkeypatched a flag, and not one RENDERED the help -- a construction-time test suite that never exercised the constructed thing, which is this repo's founding defect one layer up. CI's verify.py gate caught it, which is the check of last resort working as intended. The new test runs `verify.py --help` as a subprocess and asserts it exits 0, so it runs anywhere and NO ceiling moved. Rendering rather than grepping for `%` is deliberate: a grep would flag the legitimate `%(default)s`. FLOOR 441 -> 442 on 2026-08-23 (matched_not_invoked yields to observers and declared gates): exactly one new test, test_capabilities.test_matched_not_invoked_yields_to_observers_and_declared_gates. It is SYNTHETIC on purpose and that is the point of it: the two tests that caught this bug in the wild read the LIVE ledger, so they skipped with a named reason on the empty ledger ci.yml bootstraps -- the defect was red on every populated machine and green on CI for as long as it existed. A synthetic row asks the same question everywhere, so this one RUNS on a bare runner and NO ceiling moved. The fix itself moves matched_not_invoked below `observing` and below the DECLARED deliberately_gated check in classify_liveness: it was the first check, which made it the fourth instance of the unescapable label the comments in that function exist to fix. Audited before committing -- 12 of 43 live rows reclassify (ten observers to observing, two declared gates to deliberately_gated) and ZERO move for any other reason, so nothing is reclassified by inference and the weaker gate_reason-only branch is untouched. Measured on origin/main 0d9c3a7, whose recorded floor is 441, so 441 + 1 = 442. FLOOR RECORDED by verify.py --update-floor on 2026-08-23: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. FLOOR RECORDED by verify.py --update-floor on 2026-08-24: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. FLOOR RECORDED by verify.py --update-floor on 2026-08-24: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. MYPY_EXEMPT_MAX introduced at 64 on 2026-08-23, the same change that flipped `typecheck` ON in pr-00-gate.yml. It was OFF because 608 whole-tree errors were drainable 0 per PR -- a gate whose clear path is blocked by the thing it measures. Two real changes opened it: the src/ move scoped the Gate's `target=src` to the 99 modules (608 -> 467), and pyproject.toml's [[tool.mypy.overrides]] exempts the modules that still have findings BY NAME so the 35 already-clean ones are checked today. 12 var-annotated findings were then drained (467 -> 455, 66 -> 64 modules) to prove the drain works rather than promise it. This ceiling may only ever be LOWERED, by typing a module and deleting its line -- raising it means agreeing one more module goes unchecked, so say which and why. NOT a suppression: no error code is disabled anywhere and the 455 stay visible via `python3 scripts/ci_lint_baseline.py`. Edited BY HAND like the other *_max values; --update-floor never re-measures it. MYPY_EXEMPT_MAX LOWERED 64 -> 43 on 2026-08-24, the ratchet's first real drain: 21 modules typed clean and removed from the exempt list, 455 -> 430 findings. Targeted the modules with 1-3 errors on purpose -- fixing 50 errors spread across the big modules would move this number by ZERO, and this number is what the gate reads. Lowering it is the drain; it may never be raised without naming the module and why. MYPY_EXEMPT_MAX LOWERED 43 -> 33 on 2026-08-24 (batch 2 of the drain): the whole <=5-finding tail typed clean. 66 of 99 modules now checked. What remains is five per-module campaigns (capability_advisor, dispatcher, capability_propensity, runtime_ac_gate, capabilities hold most of it), so future batches take ONE big module at a time rather than skimming. (batch 2 detail: 43 -> 26, 430 -> 374 findings, 73 of 99 modules checked. `mypy_path` gained `tests` so mypy can RESOLVE the recurrence-fixture roster capability_admission legitimately imports; the target stays `src`, but mypy then follows into that one test file, whose two findings were fixed rather than configured around.) MYPY_EXEMPT_MAX LOWERED 26 -> 20 on 2026-08-24 (batch 3): 79 of 99 modules checked. FLOOR 442 -> 448 on 2026-08-24 (absent-check detector + its ratchet): SIX new tests in tests/test_checks_reported.py holding the frequency rule and the expected-check ratchet. NO ceiling moved -- they call pure functions and read one committed JSON file, so they run on any machine. The ratchet exists because dogfooding the detector caught it DISARMING ITSELF: while pr-00-gate.yml sat held, every merged PR merged without the Gate, so after twelve such merges the Gate's checks fell below the 75% frequency threshold, the expected set eroded 23 -> 14, and PR #91 was pronounced clean by the tool written to catch exactly that. config/expected-checks.json is the high-water mark, seeded from PRs #87/#89 whose Gate demonstrably ran, and it only comes down when somebody deletes a line. LOWERED to 16 on 2026-08-24 (batch 4, re-applied after merging main, which carried #94/#100's own floor work \u2014 main's note kept, only the bound re-set). LOWERED 16 -> 16 on 2026-08-24 (batch 5): 83 of 99 checked. LOWERED 16 -> 12 on 2026-08-24 (batch 5): 87 of 99 checked. Lesson recorded three times now: measure with the PROJECT run, never per-file \u2014 `mypy src/X.py` reports clean for modules the project run still flags. FLOOR 448 -> 450 on 2026-08-24 (giving the per-node deliberate-break finding its consumers): exactly two new pytest tests, both in test_synthesis_promotion.py -- test_a_passing_break_names_the_tautologies_it_carried and test_the_break_caveat_is_silent_on_clean_and_on_pre_per_node_evidence. NO CEILING MOVED and nothing new is skipped: the first builds its own git repo in tmp_path and runs the real verification path (git + pytest only, no populated ledger, no agent CLI, no ~/.codex), and the second is a pure unit on _break_caveat. The third piece of this change is a runtime_ac selftest case, which adds NO collected test -- runtime_ac has a --selftest and synthesis_promotion does not, which is the whole reason only one of the two needed pytest tests. Measured on the merge result: branched from origin/main 8549f84, whose recorded floor is 448, and re-checked that main had not moved before writing this. 448 + 2 = 450, from verify.py's own count (450 passed, 0 failed, 0 skipped, 85/85 selftests, 5/5 gates), not assumed. FLOOR 448 -> 451 on 2026-08-24 (offload guidance correction, PR #113): THREE new tests in test_repo_artifact_hygiene.py -- two parametrized entries adding src/UNKNOWN.egg-info/PKG-INFO and UNKNOWN.egg-info/PKG-INFO to EMITTED_ARTIFACTS, and test_no_build_metadata_is_tracked. NO ceiling moved and nothing new skips: all three shell out to git against this checkout, so they run anywhere git does. They exist because THIS PR carried four committed build artifacts and no check objected. A CI step runs `pip install -e .`, setuptools writes src/UNKNOWN.egg-info/ (named UNKNOWN because pyproject.toml declares no [project] on purpose), and the repo's own autofix bot committed all four -- .gitignore had no *.egg-info/ entry and this suite only knew the langsmith-fleet family. CodeRabbit caught it; nothing local did. Both locations are listed because a checkout builds into src/ while the EXEC MIRROR IS FLAT. The tracked test is separate from the ignored test on purpose: the four files were committed BEFORE the pattern existed, and adding a pattern does nothing to a path git already tracks -- so an ignore-only check passes on a repo still carrying the debris. RESOLVED AGAINST #110 and #114 on 2026-08-24, which moved main 448 -> 450 while this branch raised it to 451: taken as the UNION per the rule this file states -- main's entries are retained above and this branch's three-test entry beside them -- and the count RE-MEASURED on the merge result rather than either number being carried forward. 450 (main) + 3 (this branch) = 453, measured with `pytest --collect-only -q`, not assumed. Ceilings untouched and nothing new is skipped. FLOOR 453 -> 458 on 2026-08-24 (untracking .coverage, the coverage database this repo's own verify.py writes): exactly FIVE new tests, all in test_repo_artifact_hygiene.py -- two parametrized ignore cases over COVERAGE_DATA_FILES (.coverage and a parallel-mode .coverage...), one untracked case, and two parametrized must-stay-committable cases (.coveragerc, tools/coverage_guard.py). NO CEILING MOVED and nothing new is skipped: all five ask GIT about a path in this checkout (check-ignore / ls-files) and need no populated ledger, no agent CLI and no ~/.codex, so all five RUN on a bare runner. The change is `git rm --cached .coverage` plus root-anchored `/.coverage` and `/.coverage.*`; the 90 KB SQLite database arrived on main in #109, a typing PR whose every other file is about mypy, and verify.py's coverage_reset() unlinks it and coverage_combine_and_report() rewrites it on every --coverage run -- so while tracked it was an opaque binary rewritten by the command that produces this repo's verdict. Both patterns because two different steps write them, and `/.coverage.*` rather than `/.coverage*` because the second swallows .coveragerc. Break -> revert performed in all three directions and recorded in the test file. Measured on the MERGE RESULT: fast-forwarded onto origin/main 5c769e0, whose recorded floor is 453, and re-fetched to confirm main had not moved again before measuring. 453 + 5 = 458 from verify.py's own count (458 passed, 0 failed, 0 skipped, 85/85 selftests, 5/5 gates), not assumed. LOWERED 12 -> 10 on 2026-08-24 (batch 6): keepalive_outcomes (17 findings) and redirect_sweep (18) typed clean, 89 of 99 modules checked, 240 -> 205 findings. Both modules were drained by WRITE-MODE isolated offloads (dispatcher.offload --isolate) rather than in-seat, and every diff was reviewed here before it was applied -- the two changes that could have altered behaviour were checked first-person and both are equivalent: redirect_sweep's marker-rc read keeps the same except clause wrapping it, so a non-numeric rc still raises inside the try and still lands at None; keepalive_outcomes' added `oc is not None` guard short-circuits ahead of _should_record_outcome, whose own first line already returns False on oc is None. LOWERED 10 -> 7 on 2026-08-24 (batch 6b): dispatcher (65 findings), capability_propensity (47) and runtime_ac_gate (34) typed clean, 92 of 99 modules checked, 205 -> 91 findings. All three drained by write-mode isolated offloads and then INTEGRATED BY HAND rather than applied as returned -- three of the agents' choices were replaced with smaller ones, and the reasons are the durable part. (1) capability_propensity came back with two `type: ignore`s at the correlated-arm lookup, justified as an ambiguous key type. The ambiguity was real but the fix was in the wrong file: research_subjects.reciprocal_evidence_weights is GENERIC over its member type -- run-id strings at one caller, verdict indices at the other -- and its `dict[str, float]` return was too narrow. It is a TypeVar now and both ignores are gone. Suppressing there would have hidden a false positive AND blinded the one check that would catch a real key-type mismatch in the correlated-arm discount, which CLAUDE.md 2 makes load-bearing. (2) capability_propensity.detect() came back rewritten into four locals; behaviour-preserving (verified: same objects mutated, same key insertion order) but one `out: dict[str, Any]` annotation does the same job, so the annotation was taken instead. (3) runtime_ac_gate's spec_path came back with `spec_dir or env.get(K, DEFAULT)` split into branches, which silently falls back to DEFAULT where the original raised TypeError on Path(None). Unreachable via os.environ, but this is a GATE and 'silently use a default' is the permissive direction; with env typed as Mapping[str, Any] the original one-liner type-checks unchanged, so the restructure bought nothing. Asserts added in production paths were each checked against their invariant rather than trusted: dispatcher's `profile_attempt_id is not None` sits inside `elif selected_profile:` where line 815 makes it non-None by construction, and attach_profile_attempt_to_decision already declares `decision_id: str`. LOWERED 7 -> 1 on 2026-08-24 (batch 6c): capabilities (14 findings, ONE missing annotation on KNOWN_GATES caused all of them), router (16), and the last five findings across feedback/runtime_ac/capability_outcome_bridge/capability_compiler. 98 of 99 modules now checked; only capability_advisor (24) remains, held back because PR #113 edits the same file. The drain added ZERO net type: ignore -- router's one remaining suppression predates this work. THE DRAIN FOUND A REAL BUG and the value is in how it was handled: router's load_backlog promised a bare-list payload in its own docstring and could never read one, because `.get` on a list raises AttributeError into a broad `except` that returns [] -- so a populated backlog read as NO WORK. The offloaded agent was told to report suspected real bugs rather than fix them, did exactly that, and left a type: ignore documenting it; the fix landed here instead, with a selftest asserting a non-empty list survives (an empty one cannot distinguish the fix from the failure, since [] is also what a parse error returns) and a break->revert showing the old one-liner returning [] for a valid backlog. Latent, not live: the file on disk is the {\"items\": [...]} dict form, whose behaviour including its no-items fallback is unchanged. Worth closing anyway -- the day discovery writes the documented shape the symptom would be silence, which is this repo's signature failure. LOWERED 1 -> 0 on 2026-08-24 (batch 6d, THE LAST ONE): capability_advisor's 24 findings typed, the [[tool.mypy.overrides]] block removed entirely, and all 99 of 99 modules now checked. 240 findings drained to zero across the campaign with ZERO net `# type: ignore` added. THE LAST MODULE EXPOSED A LATCHED GATE IN THE RATCHET ITSELF, which is the durable finding here. verify.mypy_exempt_modules() returned None for BOTH 'pyproject unreadable' and 'readable, no override block' -- so the run that FINISHED the drain would have printed 'mypy ratchet: NOT COUNTED', the gate going silent at the exact moment it succeeded. Its own docstring already argued against this ('a ratchet that stops being counted is indistinguishable from one that emptied, and only one of those is good news'), and two more things prove it was unintended: _format_mypy_exempt_line carried a ' -- fully drained' branch no input could reach, and the selftest asserted the function was TRUTHY, so an empty list failed it. That is the CLAUDE.md pattern exactly -- a gate whose clear path is blocked by the thing it measures -- and it would have latched on the last module. Fixed: [] now means answered-and-empty, None stays unanswerable, and the selftest asserts all three renderings plus `is not None` rather than truthiness. `collected` is unchanged at 453: the new coverage lives in verify.py's own selftest, which pytest does not collect. FLOOR 500 -> 502 on 2026-08-25 by the capacity-arity branch: +2 tests, both pinning that `capacity.compute` returns THREE values on every branch. It used to return 2 or 3 depending on which of `_classify`'s 21 returns ran, and the first of those is the 429-shed check, whose `_shed` reads a file on the HOST outside `$ORCH_STATE_DIR` \u2014 so the ARITY was a property of the machine, and `test_capacity_gate_is_seat_level_not_gemini_special` died on the unpack locally while CI, where nothing is shed, stayed green. Measured on the merge result AFTER rebasing onto origin/main, which had taken #140 (500) mid-session: 502 on the rebase, not 460 on the pre-#140 branch, which is exactly the drift this equality exists to catch. MIRROR CEILING ADDED 2026-08-29: `mirror_skipped_max` = 31. `skipped_max` was ONE number bounding TWO different deprived environments, and this note above says which one it was measured in -- a GitHub runner with none of this instance's local prerequisites. The EXEC MIRROR is a different shape: every local prerequisite is present there (it exists only on the machine the system runs on, so nothing skips for a missing CLI, skill, app bundle or ledger row) but it is a FLAT FILE COPY, so it is not a git repository and has no .github/. Measured on unmodified main: 471 passed, 31 skipped -- 12 from `repo_files_absent('.github/workflows', ...)` in test_ci_gate_config.py and 19 from `git_repo_absent()` in test_repo_artifact_hygiene.py, and ZERO runner-shape skips. Against the 26 measured on a runner that skips neither family, `python3 verify.py` from ~/.codex/orchestrator-mirror was RED ON EVERY INPUT, including an entirely correct tree -- confirmed structural by syncing the PREVIOUS main into a scratch mirror and getting the same 31. CLAUDE.md 1 makes the mirror run the verdict ('cmp-clean is not agreement'), so the one instrument that catches cross-tree divergence had stopped protecting anything, and a gate red whatever you do is a gate that gets switched off. NOT FIXED BY RAISING `skipped_max` TO 31: CI runs on a runner where 26 is the correct bound, and the extra five would have let a runner skip five more checks in silence -- one gate going quiet to un-stick another. Instead each shape carries its own agreed number, selected by `env_prereq.exec_mirror_shape()`, which detects the PREREQUISITE (no .github/ AND git reports no repository) and never `$CI`, exactly as every other detector in that module does. Both marks are required because the mirror number is the LOOSER one, so an ambiguous tree falls back to the base. The mirror value REPLACES the base rather than adding to it: the runner-shape absences the base pays for cannot occur on the mirror, and 26+31 would have bought 26 units of headroom nothing there can legitimately spend. WHAT DRAINS IT: making a mirror-skipped test runnable there. Teaching orch-sync-mirror.sh to copy .github/ would drop 12 and this number must come down to 19 IN THE SAME CHANGE -- but note that script lives outside the repo, so a mirror synced by an unpatched copy would then be red at 31 > 19, which is the disease this entry cures; do it only if the script's state can be relied on. The 19 git-shape skips are irreducible while the mirror is a file copy. Any ceiling key may carry a `mirror_` variant by the same derived rule; only this one is set, and an unset variant falls back to the base agreement, which is the strict direction." + "note": "Recorded by verify.py --update-floor, except the *_max ceilings, which are edited BY HAND and never re-measured. `collected` catches tests that stopped being collected; `passed` is compared against passed+skipped, so a check may move between passing and consciously-skipped but the two together may never shrink. The *_max ceilings bound the skipped side: 24/7/2 is exactly what a machine with none of this instance's local prerequisites skips (a GitHub runner: no agent CLIs, no ~/.codex/skills, no /Applications/ChatGPT.app, no populated capability ledger), measured 2026-08-21. On the owner's machine all prerequisites exist and nothing skips at all. Raising a ceiling is a deliberate act: it means agreeing that one more thing is allowed to go unchecked, so say which and why in the commit. LOWERED 26 -> 24 on 2026-08-22, reverting the raise made earlier the same day. The two kill-switch exemption tests no longer need to skip on a bare runner: their declarations moved out of the running instance's ledger and into capabilities.KNOWN_DECLARATIONS, so they assert code-derived truth and run everywhere. Moving a test back below the ceiling is the preferred way to lower it -- fix what made it machine-dependent, rather than agreeing to check less. FLOOR 345 -> 353 on 2026-08-22: 345 was measured on a branch cut before #13 (research panels/rounds/domain studies) merged, so the recorded floor sat 8 tests BELOW what main actually collects. A floor below reality is the permissive direction -- those 8 could have silently stopped being collected and still cleared the check, which is exactly the hole this file exists to close. Measure the floor on the merge result, not on the branch. Raised again on 2026-08-22 by the producer-identity-scope branch, which adds tests on top of the 353 recorded by #15; re-measured after rebasing rather than assumed. NOTE: `verify.py --update-floor` REPLACES this note with a generic one, so it must be restored by hand after every use \u2014 the ceiling rationale is the only record of which prerequisite justifies each skip. FLOOR 365 -> 366 on 2026-08-22 (heartbeat-ordering work, PR #18): exactly one new test, test_capabilities.test_no_tick_producer_runs_above_the_heartbeat_export. No ceiling moved and nothing new is skipped -- it reads source files rather than a populated ledger, so it runs on any machine. The branch recorded 354 because it was cut before #16 merged; re-measured on the MERGE RESULT per the rule above, which is exactly the mistake that put the floor 8 below reality last time. FLOOR 366 -> 368 on 2026-08-23: main collected 368 while this file recorded 366, drift left by #34 (evidence-acquisition landed, +1) and #37 (tick capability evidence, +1) whose authors each measured against a branch cut before the other merged. A floor BELOW reality is the permissive direction this file exists to close -- those two could have silently stopped being collected and still cleared the check. Measured on the merge result per the rule above: 368 passed, 0 failed, 0 skipped, 83/83 selftests, 43/43 can-fire, 5/5 gates. CEILING 24 -> 26 and FLOOR 368 -> 387 on 2026-08-23 (profiles/provenance branch, PR #42). This file CONFLICTED with #50, which raised the floor 366 -> 368 on main while this branch raised it to 387; resolved as the UNION rather than by taking a side -- #50's rationale is retained above and the count was RE-MEASURED on the new merge result instead of keeping either number. 368 (main) + 19 (this branch's net new tests) = 387; #50 corrected recorded drift rather than adding coverage, which is why 387 is unchanged from the pre-conflict measurement. Measured in a runner sandbox reproducing CI exactly (361 passed, 26 skipped, 387 collected) AND on the owner's machine (387 passed, 0 skipped, 5/5 gates). The two new skips are drift detectors against a REAL installed agent runtime, so neither can be moved below the ceiling -- the preferred way to lower one: (1) agy advertised-models cache absent, since comparing declared model ids against the catalogue agy actually advertises needs that catalogue, and a fixture would exercise the comparison while detecting no real drift; (2) vibe config absent (~/.vibe/config.toml), since active_model cannot be read to check for drift when there is no config to read. Both name their missing prerequisite, so a green run still states what it did not check. A third candidate skip was REFUSED: dispatcher's per-run agy-log assertion failed on a bare runner because adapters.advertised_models shells out to `agy models` when its disk cache is cold, and that probe landed inside a monkeypatched subprocess.run and overwrote the captured command. That is a stub leak, so it was fixed by ISOLATING the double rather than by skipping -- which makes CI run MORE. FLOOR 387 -> 391 on 2026-08-23 (improvement-log accessor, PR #59): exactly four new tests, all in test_improvement_log.py -- three read tracked files in the tree (the pointer's size and content, and that CLAUDE.md 0 step 3 and 5 name the accessor rather than a bare path) and one runs the accessor as a subprocess against a path that cannot exist. None reads a populated ledger, an agent CLI or ~/.codex, so all four RUN on a bare runner and NO ceiling moved: nothing new is skipped. Measured on the MERGE RESULT after rebasing onto origin/main af6654d, which collected 387 -- not on the branch base, per the rule above. FLOOR 391 -> 402 on 2026-08-23 (Gate python-ci configuration, the PR that adds the missing .github/workflows/autofix-versions.env): exactly 11 new tests, all in test_ci_gate_config.py, which read committed files only -- the pin file, ruff.toml, mypy.ini, pr-00-gate.yml's toggle annotations and docs/CI_LINT_BASELINE.md. NO ceiling moved. On any CHECKOUT -- CI, the owner's tree, a second instance -- all 11 run: they need no installed linter and no populated ledger. In the EXEC-MIRROR layout all 11 skip with one named reason, because orch-sync-mirror.sh copies root-level *.py only, so .github/workflows, docs/ and scripts/ are genuinely absent there (env_prereq.repo_files_absent). That lands at 11/26 on a machine that otherwise skips nothing, and CI stays at 26/26, so no ceiling needed raising. The skip gate is the presence of those DIRECTORIES, never of the pin file itself -- gating on the file would have made the test that checks for it unable to fail. Measured on the merge result, twice: the branch was rebuilt on origin/main after #42 and #59 merged, and re-measured after #61 merged and was merged in -- 393 passed + 9 skipped = 402 collected both times, so #61 added no collected tests and this floor is not sitting below reality. #61 itself left main's floor at 391, which is exactly main-without-these-11, so there is no inherited drift to correct. RULE CHANGE 2026-08-23: `collected` is now an EQUALITY, not a minimum. Every floor entry above this one records the number being found BELOW reality and hand-raised after the fact -- 21 low at the worst, then 8, then 1, then 2 -- because nothing ever required a test-adding PR to touch this file, so the permissive direction was silent by construction and the rule 'measure on the merge result' had to be restated three times with nothing enforcing it. verify.py now FAILS when collected exceeds the floor, printing the two integers to write. That also makes the concurrency case self-enforcing: once every test-adding branch must edit these same two lines, two concurrent branches CONFLICT IN GIT, so the second cannot merge without rebasing onto the first and re-measuring on the actual merge result. Demonstrated repeatedly on the change itself: six merges landed on main in the two hours it took to write, moving this file 368 -> 387 -> 391 -> 402, and every one would have left the floor below reality under the old one-directional rule. `passed` deliberately stays a MINIMUM on passed+skipped: only collection is machine-invariant (a skipped test is still collected), measured across machines at 391 collected on both, with pass/skip splits of 365/26 on CI against 391/0 locally. The *_max ceilings are untouched by this change and nothing new is skipped. `--update-floor` also stops REPLACING this note -- it appends -- so the warning above about restoring it by hand no longer applies; and drift does NOT block --update-floor, since a gate that forbade its own only remedy would be a deadlock (the first draft was exactly that). FLOOR 402 -> 407 on 2026-08-23 (findability admission requirement). (findability admission requirement). (findability admission requirement). (findability admission requirement). Exactly five new pytest tests, all in test_capability_admission.py: test_findability_distinguishes_its_three_sub_causes, test_findability_blocks_new_capabilities_and_reports_older_ones_as_debt, test_unreadable_reach_is_not_evaluated_and_never_a_failure, test_findability_exemption_is_declared_in_code_not_in_a_live_ledger, test_consult_sites_are_falsifiable_claims_about_real_callers. NO CEILING MOVED and nothing new skips: all five build synthetic ledgers in a tempdir or read committed tables, so none needs a populated capability ledger, an agent CLI or ~/.claude/skills. The one machine-dependent thing they touch -- an external consult site declared in capability_advisor.CONSULT_SITES whose skill prompt is not on this machine -- is reported as UNVERIFIED rather than skipped, because absence of the caller is not refutation of the claim; the in-tree site (tick) is asserted verified on every machine so the check can never degrade into 'everything unverified, nothing tested'. Measured on the merge result per the rule above: this file CONFLICTED three times while the branch was open, as main went 387 -> 391 -> 402 (#61, #64, #65, #60). Each time it was resolved as the UNION rather than by taking a side, and the count was RE-MEASURED on the new merge result rather than either number being carried forward: 402 (main at bd6da2e) + 5 (this branch's new tests) = 407. That is the rule this file already states -- measure the floor on the merge result, not on the branch -- and it mattered here, because #60 both deleted test_ci_gate_config.py and added more than it removed, so guessing in either direction would have been wrong. -> re-measured on 2026-08-23 (PR #62, the four deferred #42 review findings): three new tests, all machine-independent (each builds its own tmp_path Brain and manifests), so NO CEILING MOVED and nothing new is skipped. Fourth conflict for this branch, and the first one under the EQUALITY -- which is the point: the equality's own rationale says git conflict detection is what enforces 'measure on the merge result', and that is exactly what happened here. Under the old minimum the three earlier conflicts could each have been resolved by keeping the larger number; under the equality the count MUST be measured, and it was. RESOLVED AGAINST #68 (findability admission requirement) on 2026-08-23: taken as the UNION per the rule this file states -- #68's five-test entry is retained above and this branch's three-test entry beside it -- and the count RE-MEASURED on the merge result rather than keeping either side's number. main fc1fd42 collects 407; this branch adds 3; 410 measured with `pytest --collect-only -q` on the merge result, not assumed. Ceilings untouched at 26/7/2 and nothing new is skipped. Also resolved in the same merge: langsmith-fleet-worker-attempt.json, a CI-emitted `langsmith-fleet/v1` worker-attempt record whose two sides differed only in `emitted_at` and `pr_number` (62 here, 68 on main). Main's NEWER record was kept rather than this branch's older one -- discarding a newer provenance observation to win a merge would corrupt exactly the causal-provenance evidence CLAUDE.md 2 protects, and this branch's own run re-emits its record anyway. FLOOR 410 -> 411 on 2026-08-23 (CodeRabbit follow-up on PR #42, thread 3837879039; re-measured again after #56 made `collected` an EQUALITY, which makes an assumed number a hard RED rather than a quiet pass -- main stayed at 402 across #56, and the merge result measures 403, so #56 added no collected tests and this is main's 402 plus this branch's one): exactly one new test, test_feedback_model_provenance.test_late_sweep_completes_terminal_attempts_never_one_in_flight, which pins that ledger_reconcile.resolve_unresolved_worker_attempts completes only TERMINAL unresolved worker attempts and never one still in flight. No ceiling moved and nothing new is skipped -- the test builds its own tmp ledger and codex rollout fixture and monkeypatches adapters.CODEX_SESSIONS, so it needs no agent CLI and no populated capability ledger and runs on a bare runner. RESOLVED AGAINST #59 (improvement-log accessor), which raised the floor 387 -> 391 on main while this branch raised it to 388: taken as the UNION -- #59's rationale is retained above and the count was RE-MEASURED on the new merge result rather than keeping either number, which is the rule this file states and the mistake that once put the floor 8 below reality. 391 (main, incl. #59's four tests) + 1 (this branch's one new test) = 392 measured, not assumed: 392 passed, 0 failed, 0 skipped, 83/83 selftests, 43/43 can-fire, 5/5 gates. Three sibling follow-up branches are in flight against this same main (CI/ruff config, arm-attribution + durability, adapters label->ID); if this file conflicts with one of them, resolve as the UNION and RE-MEASURE on the new merge result rather than taking either number -- that is what #42 and #50 did, and taking a side is what put the floor 8 below reality earlier. RESOLVED AGAINST #68 (findability admission requirement) on 2026-08-23: taken as the UNION per the rule this file states -- #68's five-test entry is retained above and this branch's one-test entry beside it -- and the count RE-MEASURED on the merge result. main fc1fd42 collects 407; this branch adds 1; 408 measured with `pytest --collect-only -q` on the merge result, not assumed. Ceilings untouched at 26/7/2 and nothing new is skipped -- the one new test builds its own tmp ledger and codex rollout fixture, so it runs on a bare runner. Also resolved in the same merge: langsmith-fleet-worker-attempt.json, a CI-emitted `langsmith-fleet/v1` worker-attempt record differing only in `emitted_at` and `pr_number`; main's NEWER record was kept, since discarding a newer provenance observation to win a merge would corrupt the causal-provenance evidence CLAUDE.md 2 protects. FLOOR 411 -> 415 on 2026-08-23 (PR #70 diagnostics salvage): four new collected tests in test_capability_set_coverage.py from the PR #43 salvage plus CodeRabbit follow-ups on PR #51/#70 \u2014 union/missing-candidate fetch command, truncation after six modules, AST-scoped gate-call audit, and entrypoint-diagnosis coverage. NO CEILING MOVED and nothing new is skipped; all inject synthetic ledgers or read committed source. Measured on the merge result at 91d37fa: 389 passed + 26 skipped = 415 collected on CI, not assumed. FLOOR 415 -> 416 on 2026-08-23 (the dangling-citation follow-up, PR #74): exactly ONE new test, test_ci_gate_config.test_every_cited_repo_path_resolves, which reads the two committed config files this repo OWNS (the pin file and ruff.toml) and asserts every repo-relative path they cite exists. It exists because the pin file shipped citing docs/ci/LINT_BASELINE.md when the real path was docs/CI_LINT_BASELINE.md: the sibling checks read that file's CONTENTS thoroughly and its PROSE not at all, and the prose is the only pointer telling a reader where to re-measure before bumping a pin. Scoped to the two owned files deliberately -- scanning pr-00-gate.yml yields six findings that are all correct as written (guarded by hashFiles or a .agents check, or upstream paths), and a test that cries wolf gets waived. NO ceiling moved. RE-MEASURED SIX TIMES as the base moved under this ONE-LINE change: bd6da2e 402 -> ddb0928 402 -> fc1fd42 407 -> 0d661e3 407 -> 0593eeb 411 -> 6fed4ad 415, each +1 with this test, and the branch was rebuilt on each rather than the number carried forward. THIS BRANCH IS THE WORKED EXAMPLE of the equality's concurrency cost, so record it rather than rediscover it: main moved EIGHT times in the ~2.5 hours a one-line comment fix was open (#56, #68, #73, #69, #62, #70 and two direct commits), the floor line conflicted THREE separate times, and two merges overlapped the change directly -- #73 landed a byte-identical copy of the backplane-conformance.yml guard this branch also carried (dropped as redundant), and #69 edited this very test file in a neighbouring region. The equality is still the right call and should stay: every entry above this one records the floor being found BELOW reality, which is the permissive direction. But no amount of author care wins this race, because the correct value is only knowable on the merge result. The durable fix is CI running `verify.py --update-floor` on the merge commit, which keeps the equality and removes the race; until then a test-adding PR must be merged promptly after going green, because it re-conflicts on roughly every subsequent merge. FLOOR 416 -> 427 on 2026-08-23 (PR #72 hygiene untrack, rebased after #71 merged): exactly 11 new tests from test_repo_artifact_hygiene.py with root-anchored gitignore patterns. NO ceiling moved. Measured on merge result after #71 landed on main: 416 (main) + 11 = 427 collected via pytest --collect-only -q, not assumed. #71's simpler untrack landed first; this branch carries the full hygiene test suite and corrected root-anchored patterns. FLOOR 427 -> 428 on 2026-08-23 (PR salvaging #34/#42 remnants): exactly one new test, test_feedback_model_provenance.test_gemini_provenance_reads_the_per_run_log_before_the_conversation_store, recovered from #42's post-merge commit 4e0d6ae along with the adapters catalog work it exercises. No ceiling moved and nothing new is skipped -- it seeds adapters._ADVERTISED_MEMO instead of letting the catalog probe shell out, so it runs on any machine and adds no prerequisite. `passed` is 428 rather than the 426 verify.py suggested on this machine: two test_capabilities liveness tests (test_gate_blocks_execution_is_opt_in_and_narrow, test_evidence_gate_kind_is_not_blanket_observer) currently fail HERE on pristine main as well, because the hourly fleet tick mutated the machine-local ledger and range-lane-rollout now classifies matched_not_invoked rather than deliberately_gated. That is ledger STATE, not this branch and not the code -- CI bootstraps an empty ledger and counts 428/428. Recording 426 would have baked a local environment failure into the floor as though it were the expected result. FLOOR 428 -> 441 on 2026-08-23 (coverage measures what actually runs): exactly 12 new tests, all in test_verify_coverage_mode.py. They read committed files and verify.py's own source, and monkeypatch verify.COVERAGE in-process -- no populated ledger, no agent CLI, no ~/.codex, and no coverage RUN -- so all 12 execute on any machine and NO ceiling moved. The change itself is a measurement fix, not a gate: `verify.py --coverage` wraps each child in `coverage run --parallel-mode` and combines, because the per-module --selftest is a SUBPROCESS and a pytest-only coverage run cannot see it. That blind spot was most of the codebase -- 78 modules have no test_*.py at all, ~85,500 lines, 79.6% of non-test root Python -- so the reported 48.45% was measuring the gap in the instrument, not a gap in the tests. Combined: 76.1% (45,049 statements, 10,774 missed). Twelve of the twelve modules the old report named as worst were selftest-only; outcomes.py reported 9.0% and measures 61.9%, watch.py 9.6% -> 86.9%. Coverage is OFF by default and deliberately never touches the exit code -- one of the 12 tests pins that, because enforcing a threshold here would reward pytest wrappers around already-tested modules: metric up, assurance flat. Measured on the MERGE RESULT: branched from origin/main bcc68cd (floor 427), then REBASED onto f5f1c39 when it landed underneath and re-measured on the new merge result rather than carrying the old number: 428 + 13 = 441. +1 on 2026-08-23 (440 -> 441 after the rebase), same branch: test_the_cli_help_actually_renders. It exists because this branch BROKE `verify.py --help` and its own twelve tests did not notice. argparse interpolates help strings with `% params`, so the literal `~80%` in the --coverage help was read as an `%o` octal conversion and --help died with 'badly formed help string'. All twelve original tests passed: every one inspected source text or monkeypatched a flag, and not one RENDERED the help -- a construction-time test suite that never exercised the constructed thing, which is this repo's founding defect one layer up. CI's verify.py gate caught it, which is the check of last resort working as intended. The new test runs `verify.py --help` as a subprocess and asserts it exits 0, so it runs anywhere and NO ceiling moved. Rendering rather than grepping for `%` is deliberate: a grep would flag the legitimate `%(default)s`. FLOOR 441 -> 442 on 2026-08-23 (matched_not_invoked yields to observers and declared gates): exactly one new test, test_capabilities.test_matched_not_invoked_yields_to_observers_and_declared_gates. It is SYNTHETIC on purpose and that is the point of it: the two tests that caught this bug in the wild read the LIVE ledger, so they skipped with a named reason on the empty ledger ci.yml bootstraps -- the defect was red on every populated machine and green on CI for as long as it existed. A synthetic row asks the same question everywhere, so this one RUNS on a bare runner and NO ceiling moved. The fix itself moves matched_not_invoked below `observing` and below the DECLARED deliberately_gated check in classify_liveness: it was the first check, which made it the fourth instance of the unescapable label the comments in that function exist to fix. Audited before committing -- 12 of 43 live rows reclassify (ten observers to observing, two declared gates to deliberately_gated) and ZERO move for any other reason, so nothing is reclassified by inference and the weaker gate_reason-only branch is untouched. Measured on origin/main 0d9c3a7, whose recorded floor is 441, so 441 + 1 = 442. FLOOR RECORDED by verify.py --update-floor on 2026-08-23: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. FLOOR RECORDED by verify.py --update-floor on 2026-08-24: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. FLOOR RECORDED by verify.py --update-floor on 2026-08-24: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. MYPY_EXEMPT_MAX introduced at 64 on 2026-08-23, the same change that flipped `typecheck` ON in pr-00-gate.yml. It was OFF because 608 whole-tree errors were drainable 0 per PR -- a gate whose clear path is blocked by the thing it measures. Two real changes opened it: the src/ move scoped the Gate's `target=src` to the 99 modules (608 -> 467), and pyproject.toml's [[tool.mypy.overrides]] exempts the modules that still have findings BY NAME so the 35 already-clean ones are checked today. 12 var-annotated findings were then drained (467 -> 455, 66 -> 64 modules) to prove the drain works rather than promise it. This ceiling may only ever be LOWERED, by typing a module and deleting its line -- raising it means agreeing one more module goes unchecked, so say which and why. NOT a suppression: no error code is disabled anywhere and the 455 stay visible via `python3 scripts/ci_lint_baseline.py`. Edited BY HAND like the other *_max values; --update-floor never re-measures it. MYPY_EXEMPT_MAX LOWERED 64 -> 43 on 2026-08-24, the ratchet's first real drain: 21 modules typed clean and removed from the exempt list, 455 -> 430 findings. Targeted the modules with 1-3 errors on purpose -- fixing 50 errors spread across the big modules would move this number by ZERO, and this number is what the gate reads. Lowering it is the drain; it may never be raised without naming the module and why. MYPY_EXEMPT_MAX LOWERED 43 -> 33 on 2026-08-24 (batch 2 of the drain): the whole <=5-finding tail typed clean. 66 of 99 modules now checked. What remains is five per-module campaigns (capability_advisor, dispatcher, capability_propensity, runtime_ac_gate, capabilities hold most of it), so future batches take ONE big module at a time rather than skimming. (batch 2 detail: 43 -> 26, 430 -> 374 findings, 73 of 99 modules checked. `mypy_path` gained `tests` so mypy can RESOLVE the recurrence-fixture roster capability_admission legitimately imports; the target stays `src`, but mypy then follows into that one test file, whose two findings were fixed rather than configured around.) MYPY_EXEMPT_MAX LOWERED 26 -> 20 on 2026-08-24 (batch 3): 79 of 99 modules checked. FLOOR 442 -> 448 on 2026-08-24 (absent-check detector + its ratchet): SIX new tests in tests/test_checks_reported.py holding the frequency rule and the expected-check ratchet. NO ceiling moved -- they call pure functions and read one committed JSON file, so they run on any machine. The ratchet exists because dogfooding the detector caught it DISARMING ITSELF: while pr-00-gate.yml sat held, every merged PR merged without the Gate, so after twelve such merges the Gate's checks fell below the 75% frequency threshold, the expected set eroded 23 -> 14, and PR #91 was pronounced clean by the tool written to catch exactly that. config/expected-checks.json is the high-water mark, seeded from PRs #87/#89 whose Gate demonstrably ran, and it only comes down when somebody deletes a line. LOWERED to 16 on 2026-08-24 (batch 4, re-applied after merging main, which carried #94/#100's own floor work \u2014 main's note kept, only the bound re-set). LOWERED 16 -> 16 on 2026-08-24 (batch 5): 83 of 99 checked. LOWERED 16 -> 12 on 2026-08-24 (batch 5): 87 of 99 checked. Lesson recorded three times now: measure with the PROJECT run, never per-file \u2014 `mypy src/X.py` reports clean for modules the project run still flags. FLOOR 448 -> 450 on 2026-08-24 (giving the per-node deliberate-break finding its consumers): exactly two new pytest tests, both in test_synthesis_promotion.py -- test_a_passing_break_names_the_tautologies_it_carried and test_the_break_caveat_is_silent_on_clean_and_on_pre_per_node_evidence. NO CEILING MOVED and nothing new is skipped: the first builds its own git repo in tmp_path and runs the real verification path (git + pytest only, no populated ledger, no agent CLI, no ~/.codex), and the second is a pure unit on _break_caveat. The third piece of this change is a runtime_ac selftest case, which adds NO collected test -- runtime_ac has a --selftest and synthesis_promotion does not, which is the whole reason only one of the two needed pytest tests. Measured on the merge result: branched from origin/main 8549f84, whose recorded floor is 448, and re-checked that main had not moved before writing this. 448 + 2 = 450, from verify.py's own count (450 passed, 0 failed, 0 skipped, 85/85 selftests, 5/5 gates), not assumed. FLOOR 448 -> 451 on 2026-08-24 (offload guidance correction, PR #113): THREE new tests in test_repo_artifact_hygiene.py -- two parametrized entries adding src/UNKNOWN.egg-info/PKG-INFO and UNKNOWN.egg-info/PKG-INFO to EMITTED_ARTIFACTS, and test_no_build_metadata_is_tracked. NO ceiling moved and nothing new skips: all three shell out to git against this checkout, so they run anywhere git does. They exist because THIS PR carried four committed build artifacts and no check objected. A CI step runs `pip install -e .`, setuptools writes src/UNKNOWN.egg-info/ (named UNKNOWN because pyproject.toml declares no [project] on purpose), and the repo's own autofix bot committed all four -- .gitignore had no *.egg-info/ entry and this suite only knew the langsmith-fleet family. CodeRabbit caught it; nothing local did. Both locations are listed because a checkout builds into src/ while the EXEC MIRROR IS FLAT. The tracked test is separate from the ignored test on purpose: the four files were committed BEFORE the pattern existed, and adding a pattern does nothing to a path git already tracks -- so an ignore-only check passes on a repo still carrying the debris. RESOLVED AGAINST #110 and #114 on 2026-08-24, which moved main 448 -> 450 while this branch raised it to 451: taken as the UNION per the rule this file states -- main's entries are retained above and this branch's three-test entry beside them -- and the count RE-MEASURED on the merge result rather than either number being carried forward. 450 (main) + 3 (this branch) = 453, measured with `pytest --collect-only -q`, not assumed. Ceilings untouched and nothing new is skipped. FLOOR 453 -> 458 on 2026-08-24 (untracking .coverage, the coverage database this repo's own verify.py writes): exactly FIVE new tests, all in test_repo_artifact_hygiene.py -- two parametrized ignore cases over COVERAGE_DATA_FILES (.coverage and a parallel-mode .coverage...), one untracked case, and two parametrized must-stay-committable cases (.coveragerc, tools/coverage_guard.py). NO CEILING MOVED and nothing new is skipped: all five ask GIT about a path in this checkout (check-ignore / ls-files) and need no populated ledger, no agent CLI and no ~/.codex, so all five RUN on a bare runner. The change is `git rm --cached .coverage` plus root-anchored `/.coverage` and `/.coverage.*`; the 90 KB SQLite database arrived on main in #109, a typing PR whose every other file is about mypy, and verify.py's coverage_reset() unlinks it and coverage_combine_and_report() rewrites it on every --coverage run -- so while tracked it was an opaque binary rewritten by the command that produces this repo's verdict. Both patterns because two different steps write them, and `/.coverage.*` rather than `/.coverage*` because the second swallows .coveragerc. Break -> revert performed in all three directions and recorded in the test file. Measured on the MERGE RESULT: fast-forwarded onto origin/main 5c769e0, whose recorded floor is 453, and re-fetched to confirm main had not moved again before measuring. 453 + 5 = 458 from verify.py's own count (458 passed, 0 failed, 0 skipped, 85/85 selftests, 5/5 gates), not assumed. LOWERED 12 -> 10 on 2026-08-24 (batch 6): keepalive_outcomes (17 findings) and redirect_sweep (18) typed clean, 89 of 99 modules checked, 240 -> 205 findings. Both modules were drained by WRITE-MODE isolated offloads (dispatcher.offload --isolate) rather than in-seat, and every diff was reviewed here before it was applied -- the two changes that could have altered behaviour were checked first-person and both are equivalent: redirect_sweep's marker-rc read keeps the same except clause wrapping it, so a non-numeric rc still raises inside the try and still lands at None; keepalive_outcomes' added `oc is not None` guard short-circuits ahead of _should_record_outcome, whose own first line already returns False on oc is None. LOWERED 10 -> 7 on 2026-08-24 (batch 6b): dispatcher (65 findings), capability_propensity (47) and runtime_ac_gate (34) typed clean, 92 of 99 modules checked, 205 -> 91 findings. All three drained by write-mode isolated offloads and then INTEGRATED BY HAND rather than applied as returned -- three of the agents' choices were replaced with smaller ones, and the reasons are the durable part. (1) capability_propensity came back with two `type: ignore`s at the correlated-arm lookup, justified as an ambiguous key type. The ambiguity was real but the fix was in the wrong file: research_subjects.reciprocal_evidence_weights is GENERIC over its member type -- run-id strings at one caller, verdict indices at the other -- and its `dict[str, float]` return was too narrow. It is a TypeVar now and both ignores are gone. Suppressing there would have hidden a false positive AND blinded the one check that would catch a real key-type mismatch in the correlated-arm discount, which CLAUDE.md 2 makes load-bearing. (2) capability_propensity.detect() came back rewritten into four locals; behaviour-preserving (verified: same objects mutated, same key insertion order) but one `out: dict[str, Any]` annotation does the same job, so the annotation was taken instead. (3) runtime_ac_gate's spec_path came back with `spec_dir or env.get(K, DEFAULT)` split into branches, which silently falls back to DEFAULT where the original raised TypeError on Path(None). Unreachable via os.environ, but this is a GATE and 'silently use a default' is the permissive direction; with env typed as Mapping[str, Any] the original one-liner type-checks unchanged, so the restructure bought nothing. Asserts added in production paths were each checked against their invariant rather than trusted: dispatcher's `profile_attempt_id is not None` sits inside `elif selected_profile:` where line 815 makes it non-None by construction, and attach_profile_attempt_to_decision already declares `decision_id: str`. LOWERED 7 -> 1 on 2026-08-24 (batch 6c): capabilities (14 findings, ONE missing annotation on KNOWN_GATES caused all of them), router (16), and the last five findings across feedback/runtime_ac/capability_outcome_bridge/capability_compiler. 98 of 99 modules now checked; only capability_advisor (24) remains, held back because PR #113 edits the same file. The drain added ZERO net type: ignore -- router's one remaining suppression predates this work. THE DRAIN FOUND A REAL BUG and the value is in how it was handled: router's load_backlog promised a bare-list payload in its own docstring and could never read one, because `.get` on a list raises AttributeError into a broad `except` that returns [] -- so a populated backlog read as NO WORK. The offloaded agent was told to report suspected real bugs rather than fix them, did exactly that, and left a type: ignore documenting it; the fix landed here instead, with a selftest asserting a non-empty list survives (an empty one cannot distinguish the fix from the failure, since [] is also what a parse error returns) and a break->revert showing the old one-liner returning [] for a valid backlog. Latent, not live: the file on disk is the {\"items\": [...]} dict form, whose behaviour including its no-items fallback is unchanged. Worth closing anyway -- the day discovery writes the documented shape the symptom would be silence, which is this repo's signature failure. LOWERED 1 -> 0 on 2026-08-24 (batch 6d, THE LAST ONE): capability_advisor's 24 findings typed, the [[tool.mypy.overrides]] block removed entirely, and all 99 of 99 modules now checked. 240 findings drained to zero across the campaign with ZERO net `# type: ignore` added. THE LAST MODULE EXPOSED A LATCHED GATE IN THE RATCHET ITSELF, which is the durable finding here. verify.mypy_exempt_modules() returned None for BOTH 'pyproject unreadable' and 'readable, no override block' -- so the run that FINISHED the drain would have printed 'mypy ratchet: NOT COUNTED', the gate going silent at the exact moment it succeeded. Its own docstring already argued against this ('a ratchet that stops being counted is indistinguishable from one that emptied, and only one of those is good news'), and two more things prove it was unintended: _format_mypy_exempt_line carried a ' -- fully drained' branch no input could reach, and the selftest asserted the function was TRUTHY, so an empty list failed it. That is the CLAUDE.md pattern exactly -- a gate whose clear path is blocked by the thing it measures -- and it would have latched on the last module. Fixed: [] now means answered-and-empty, None stays unanswerable, and the selftest asserts all three renderings plus `is not None` rather than truthiness. `collected` is unchanged at 453: the new coverage lives in verify.py's own selftest, which pytest does not collect. FLOOR 500 -> 502 on 2026-08-25 by the capacity-arity branch: +2 tests, both pinning that `capacity.compute` returns THREE values on every branch. It used to return 2 or 3 depending on which of `_classify`'s 21 returns ran, and the first of those is the 429-shed check, whose `_shed` reads a file on the HOST outside `$ORCH_STATE_DIR` \u2014 so the ARITY was a property of the machine, and `test_capacity_gate_is_seat_level_not_gemini_special` died on the unpack locally while CI, where nothing is shed, stayed green. Measured on the merge result AFTER rebasing onto origin/main, which had taken #140 (500) mid-session: 502 on the rebase, not 460 on the pre-#140 branch, which is exactly the drift this equality exists to catch. MIRROR CEILING ADDED 2026-08-29: `mirror_skipped_max` = 31. `skipped_max` was ONE number bounding TWO different deprived environments, and this note above says which one it was measured in -- a GitHub runner with none of this instance's local prerequisites. The EXEC MIRROR is a different shape: every local prerequisite is present there (it exists only on the machine the system runs on, so nothing skips for a missing CLI, skill, app bundle or ledger row) but it is a FLAT FILE COPY, so it is not a git repository and has no .github/. Measured on unmodified main: 471 passed, 31 skipped -- 12 from `repo_files_absent('.github/workflows', ...)` in test_ci_gate_config.py and 19 from `git_repo_absent()` in test_repo_artifact_hygiene.py, and ZERO runner-shape skips. Against the 26 measured on a runner that skips neither family, `python3 verify.py` from ~/.codex/orchestrator-mirror was RED ON EVERY INPUT, including an entirely correct tree -- confirmed structural by syncing the PREVIOUS main into a scratch mirror and getting the same 31. CLAUDE.md 1 makes the mirror run the verdict ('cmp-clean is not agreement'), so the one instrument that catches cross-tree divergence had stopped protecting anything, and a gate red whatever you do is a gate that gets switched off. NOT FIXED BY RAISING `skipped_max` TO 31: CI runs on a runner where 26 is the correct bound, and the extra five would have let a runner skip five more checks in silence -- one gate going quiet to un-stick another. Instead each shape carries its own agreed number, selected by `env_prereq.exec_mirror_shape()`, which detects the PREREQUISITE (no .github/ AND git reports no repository) and never `$CI`, exactly as every other detector in that module does. Both marks are required because the mirror number is the LOOSER one, so an ambiguous tree falls back to the base. The mirror value REPLACES the base rather than adding to it: the runner-shape absences the base pays for cannot occur on the mirror, and 26+31 would have bought 26 units of headroom nothing there can legitimately spend. WHAT DRAINS IT: making a mirror-skipped test runnable there. Teaching orch-sync-mirror.sh to copy .github/ would drop 12 and this number must come down to 19 IN THE SAME CHANGE -- but note that script lives outside the repo, so a mirror synced by an unpatched copy would then be red at 31 > 19, which is the disease this entry cures; do it only if the script's state can be relied on. The 19 git-shape skips are irreducible while the mirror is a file copy. Any ceiling key may carry a `mirror_` variant by the same derived rule; only this one is set, and an unset variant falls back to the base agreement, which is the strict direction. FLOOR 502 -> 515 on 2026-08-26 (escaped-defect test priority): exactly THIRTEEN new tests, all in tests/test_escaped_defect_priority.py. NO CEILING MOVED and nothing new is skipped: each builds its own git repository in tmp_path or calls a pure function, so none needs a populated ledger, an agent CLI or ~/.codex, and all thirteen run on a bare runner. They are pytest rather than selftest cases ON PURPOSE, and the reason is the subject of the module: local_verify grades per pytest NODE, so a selftest is one node and its internal assertions are invisible to hollow-test detection -- a module that orders test-writing work should have its own tests gradeable by the gate that judges the work it orders. The module ALSO keeps a --selftest (88 of 88 now), which exercises the CLI the way it ships including live git log parsing; the two are complementary, not alternatives. Two of the thirteen caught a real defect in the module during review: the first implementation multiplied the tiers apart (1e6/1e3/1) and summed them, which holds only while the lower tiers stay small -- at 1,000,000 uncovered statements tier 3 exactly equals one escaped defect and the ordering the module exists to guarantee silently inverts. Replaced with a lexicographic tuple, which holds at any magnitude, and the tests now assert at 10**9 rather than a modest number. Measured on the MERGE RESULT: branched from origin/main b80e6e4, whose recorded floor is 502, and re-fetched to confirm main had not moved before measuring. 502 + 13 = 515 from verify.py's own count (515 passed, 0 failed, 0 skipped, 88/88 selftests, 5/5 gates), not assumed." } diff --git a/src/escaped_defect_priority.py b/src/escaped_defect_priority.py new file mode 100644 index 0000000..4a2961a --- /dev/null +++ b/src/escaped_defect_priority.py @@ -0,0 +1,441 @@ +#!/usr/bin/env python3 +"""escaped_defect_priority.py — order test-writing work by where testing actually FAILED. + +WHY NOT "MOST UNCOVERED LINES FIRST". That ordering maximises percentage-per-PR, and it points +agents at the largest uncovered files, which are almost always the big glue modules where a +meaningful test is hardest to write. It is the ordering most likely to produce hollow tests, and +hollow tests raise the number while buying nothing. So uncovered mass is the LAST tier here, not +the first. + +THREE TIERS, most informative first. + + 1. ESCAPED DEFECTS — a file that later needed a bug fix is a file whose tests did not catch + something. This is the only tier that reports observed failure of the tests themselves rather + than a property of the code, which is why it leads. + 2. CHURN x TESTABILITY — a file that changes often is where regressions arrive; one with a low + branch-to-statement ratio is one where a test can pin behaviour rather than smoke it. + 3. UNCOVERED MASS — how much the metric would move. Last, deliberately. + + Every tier is multiplied by (1 - hollow_rate). A module where agents keep producing tests that + pass against a broken base scores LOW however much uncovered code it has, so the untestable glue + de-prioritises itself without anyone classifying it by hand. That factor only became measurable + when testgen_gate grew `no_hollow_nodes`; before it, there was no way to tell the two apart. + +TIER 1 IS A GIT PROXY TODAY, AND SAYS SO. The Brain has the better signal in +`outcomes.durability` — `broke_later` means merged, CI green, broke afterwards. Measured +2026-08-26 across 4,665 outcome rows: durable 2842, abandoned 1300, pending 517, reverted 4, +reworked 2, and `broke_later` ZERO. `durability_sweep.py` assigns "reopened, reverted, or durable" +and never `broke_later`, while `pattern_miner.TERMINAL_FAILURE_DURABILITY` consumes it — a +consumer for a label nothing produces. Six escaped-defect rows cannot order a work queue, so tier +1 reads git history instead: it is available in every repo today and needs no instrumentation. +`brain_signal_status()` reports which source is in use, so the day the Brain signal becomes usable +is visible rather than assumed. + +CONFIDENCE IS NOT UNIFORM, AND THE DIFFERENCE MATTERS. A `fix(...)` commit touching a file is +decent evidence for ORDERING work and poor evidence for TRAINING a learner: code is changed for +many reasons, and a fix on the same file may repair something the original change never touched. +So this module ranks; it does NOT write durability labels. Anything feeding `outcomes.durability` +must clear a higher bar, and keeping the two apart is deliberate. +""" + +from __future__ import annotations + +import argparse +import json +import os +import re +import subprocess +import sys +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +KILL_SWITCH = "ORCH_ESCAPED_DEFECT_PRIORITY" + +# Conventional-commit fix prefixes, plus the bare forms real history actually uses. Anchored at the +# start of the subject so "prefix the fix with..." in prose does not count as a fix commit. +FIX_SUBJECT = re.compile( + r"^\s*(?:fix|bugfix|hotfix|patch)\b[^:]*:|^\s*(?:fix|fixes|fixed)\s+", + re.IGNORECASE, +) +REVERT_SUBJECT = re.compile(r"^\s*revert\b", re.IGNORECASE) + +# A revert is stronger evidence than an ordinary fix: somebody judged the change wrong outright. +REVERT_WEIGHT = 3.0 +FIX_WEIGHT = 1.0 + +DEFAULT_LOOKBACK_DAYS = 180 +DEFAULT_LIMIT = 25 + + +@dataclass +class FileScore: + """One candidate file, with every tier kept separate so a ranking can be explained.""" + + path: str + escaped: float = 0.0 + churn: int = 0 + uncovered: int = 0 + hollow_rate: float = 0.0 + evidence: list[str] = field(default_factory=list) + + def sort_key(self) -> tuple[float, float, float]: + """LEXICOGRAPHIC, not a weighted sum — the tiers must not trade against each other. + + The first version multiplied the tiers apart (1e6 / 1e3 / 1) and summed them, which only + holds while the lower tiers stay small: at 1,000,000 uncovered statements tier 3 exactly + equals one escaped defect, and the ordering the module exists to guarantee silently + inverts. A scoring function whose correctness depends on its inputs staying below a magic + threshold is a defect waiting for a big repository, so the comparison is a tuple: no + amount of tier 3 can ever reach past tier 2, whatever the magnitudes. + + The hollow discount multiplies EVERY component rather than the total, so a fully hollow + file collapses to (0, 0, 0) and sorts last regardless of tier, while a partly hollow one + keeps its tier and is discounted within it. + """ + keep = 1.0 - self.hollow_rate + return (self.escaped * keep, self.churn * keep, self.uncovered * keep) + + def as_dict(self) -> dict[str, Any]: + # No blended "score" is reported. One number formed from three incomparable tiers invites + # exactly the trade-off the tuple exists to forbid, and a reader can order these rows by + # eye without it. + t1, t2, t3 = self.sort_key() + return { + "path": self.path, + "rank_key": [round(t1, 3), round(t2, 3), round(t3, 3)], + "tier1_escaped_defects": round(self.escaped, 3), + "tier2_churn": self.churn, + "tier3_uncovered_statements": self.uncovered, + "hollow_rate": round(self.hollow_rate, 3), + "evidence": self.evidence[:5], + } + + +def _git(repo: Path, *args: str, timeout: int = 60) -> str: + proc = subprocess.run( + ["git", *args], cwd=repo, capture_output=True, text=True, timeout=timeout, check=False + ) + return proc.stdout if proc.returncode == 0 else "" + + +def fix_commits(repo: Path, lookback_days: int = DEFAULT_LOOKBACK_DAYS) -> list[tuple[str, float]]: + """Return (file, weight) for every file touched by a fix or revert commit in the window. + + `--no-merges` matters: a squash-merge commit carries the PR title, so counting merges as well + would double-count the same fix once as the merge and once as the underlying commit. + """ + raw = _git( + repo, + "log", + f"--since={lookback_days}.days.ago", + "--no-merges", + "--name-only", + "--pretty=format:%x00%s", + ) + out: list[tuple[str, float]] = [] + weight = 0.0 + for line in raw.split("\n"): + if line.startswith("\x00"): + subject = line[1:] + if REVERT_SUBJECT.search(subject): + weight = REVERT_WEIGHT + elif FIX_SUBJECT.search(subject): + weight = FIX_WEIGHT + else: + weight = 0.0 + continue + path = line.strip() + if path and weight: + out.append((path, weight)) + return out + + +def churn(repo: Path, lookback_days: int = DEFAULT_LOOKBACK_DAYS) -> dict[str, int]: + """How many non-merge commits touched each file in the window.""" + raw = _git( + repo, + "log", + f"--since={lookback_days}.days.ago", + "--no-merges", + "--name-only", + "--pretty=format:", + ) + counts: dict[str, int] = {} + for line in raw.split("\n"): + path = line.strip() + if path: + counts[path] = counts.get(path, 0) + 1 + return counts + + +def uncovered_by_file(coverage_json: dict[str, Any]) -> dict[str, int]: + """Missing statements per file, from a coverage.py JSON report. + + Rows with an ABSOLUTE path are dropped: those are tmp-workspace copies a test fixture made, + and they are not files anybody can open. That contamination put 95 phantom rows into + stranske/Trend_Model_Project's payload and filled 13 of its 15 worst-file slots. + """ + out: dict[str, int] = {} + for path, data in (coverage_json.get("files") or {}).items(): + if Path(path).is_absolute(): + continue + summary = data.get("summary") or {} + missing = int(summary.get("missing_lines") or 0) + if missing: + out[path] = missing + return out + + +def rank( + repo: str | Path, + coverage_json: dict[str, Any] | None = None, + *, + hollow_rates: dict[str, float] | None = None, + lookback_days: int = DEFAULT_LOOKBACK_DAYS, + limit: int = DEFAULT_LIMIT, +) -> list[dict[str, Any]]: + """Rank candidate files for test-writing, most informative signal first. + + `hollow_rates` maps path -> observed share of hollow nodes from past testgen attempts. Absent, + every file scores as if no attempt has been made, which is the correct prior: unmeasured is not + the same as measured-good, and the first attempt on a file is what produces the measurement. + """ + repo_path = Path(repo).expanduser().resolve() + scores: dict[str, FileScore] = {} + + def _row(path: str) -> FileScore: + if path not in scores: + scores[path] = FileScore(path=path) + return scores[path] + + for path, weight in fix_commits(repo_path, lookback_days): + row = _row(path) + row.escaped += weight + label = "revert" if weight == REVERT_WEIGHT else "fix" + if len(row.evidence) < 5: + row.evidence.append(f"{label} commit touched this file") + + for path, count in churn(repo_path, lookback_days).items(): + _row(path).churn = count + + for path, missing in uncovered_by_file(coverage_json or {}).items(): + _row(path).uncovered = missing + + for path, rate in (hollow_rates or {}).items(): + if path in scores: + scores[path].hollow_rate = max(0.0, min(1.0, float(rate))) + + # Only Python source is a testgen target. Ranking a lockfile by churn would be true and useless. + ordered = [ + s + for s in scores.values() + if s.path.endswith(".py") and not Path(s.path).name.startswith("test_") + ] + ordered.sort(key=lambda s: (-s.sort_key()[0], -s.sort_key()[1], -s.sort_key()[2], s.path)) + return [s.as_dict() for s in ordered[:limit]] + + +def brain_signal_status(db_path: str | Path | None = None) -> dict[str, Any]: + """Report whether the Brain's escaped-defect signal is usable yet, and never guess. + + Tier 1 reads git today because `outcomes.durability` carries almost no terminal-failure rows. + This makes that a measured statement rather than an assumption, so the day it becomes usable is + visible. An unreadable Brain reports `unknown` -- NOT zero, because "no data" and "cannot read" + are different answers and only one of them means the signal is absent. + """ + import sqlite3 + + if db_path is None: + try: + import feedback + + db_path = getattr(feedback, "DB", None) or getattr(feedback, "DB_PATH", None) + except Exception: + db_path = None + if not db_path or not Path(str(db_path)).exists(): + return {"source": "git", "brain": "unknown", "reason": "feedback store not readable here"} + try: + conn = sqlite3.connect(str(db_path)) + rows = dict(conn.execute("SELECT durability, COUNT(*) FROM outcomes GROUP BY durability")) + except Exception as exc: # noqa: BLE001 - a broken read must not take the ranking down + return {"source": "git", "brain": "unknown", "reason": f"query failed: {exc}"} + terminal = sum( + int(rows.get(k) or 0) for k in ("broke_later", "reopened", "reverted", "reworked") + ) + return { + "source": "git" if terminal < 30 else "brain", + "brain": "sparse" if terminal < 30 else "usable", + "terminal_failure_rows": terminal, + "broke_later_rows": int(rows.get("broke_later") or 0), + "reason": ( + "fewer than 30 terminal-failure outcomes: too sparse to order a queue, so tier 1 " + "falls back to git history" + if terminal < 30 + else "enough terminal-failure outcomes to rank on directly" + ), + } + + +def enabled() -> bool: + """The kill switch. Unset or 0 means the caller keeps its previous ordering.""" + return os.environ.get(KILL_SWITCH, "") == "1" + + +def _selftest() -> None: + import tempfile + + # --- tier separation: one escaped defect outranks a large uncovered file ----------------- + defect = FileScore(path="a.py", escaped=1.0, churn=0, uncovered=0) + # DELIBERATELY ABSURD magnitudes: the weighted-sum version passed at 5,000 and inverted at + # 1,000,000, so the selftest uses the number that actually breaks a scaled sum. + bulk = FileScore(path="b.py", escaped=0.0, churn=0, uncovered=10**9) + assert defect.sort_key() > bulk.sort_key(), (defect.sort_key(), bulk.sort_key()) + churny = FileScore(path="c.py", escaped=0.0, churn=50, uncovered=0) + assert churny.sort_key() > bulk.sort_key(), "churn must outrank raw uncovered mass" + assert defect.sort_key() > churny.sort_key(), "an escaped defect must outrank churn" + + # --- the hollow multiplier sinks a file however much uncovered code it has --------------- + hollow = FileScore(path="d.py", escaped=1.0, uncovered=0, hollow_rate=1.0) + assert hollow.sort_key() == (0.0, 0.0, 0.0), hollow.sort_key() + half = FileScore(path="e.py", escaped=1.0, hollow_rate=0.5) + assert abs(half.sort_key()[0] - defect.sort_key()[0] / 2) < 1e-6 + + # --- subject matching: prose about fixing is not a fix commit --------------------------- + assert FIX_SUBJECT.search("fix(coverage): omit tmp rows") + assert FIX_SUBJECT.search("fix: null deref") + assert FIX_SUBJECT.search("Fixes crash on empty input") + assert not FIX_SUBJECT.search("feat: prefix the fix with a scope") + assert not FIX_SUBJECT.search("refactor: tidy the fixer") + assert REVERT_SUBJECT.search('Revert "feat: thing"') + + # --- absolute paths are contamination, not candidates ------------------------------------ + cov = { + "files": { + "src/real.py": {"summary": {"missing_lines": 10}}, + "/tmp/pytest-of-runner/ws/src/real.py": {"summary": {"missing_lines": 900}}, + "src/clean.py": {"summary": {"missing_lines": 0}}, + } + } + unc = uncovered_by_file(cov) + assert unc == {"src/real.py": 10}, unc + + # --- end to end against a REAL git repo, so the log parsing is exercised, not mocked ------ + with tempfile.TemporaryDirectory() as td: + repo = Path(td) + subprocess.run(["git", "init", "-q", "."], cwd=repo, check=True) + subprocess.run(["git", "config", "user.email", "a@b.c"], cwd=repo, check=True) + subprocess.run(["git", "config", "user.name", "t"], cwd=repo, check=True) + (repo / "buggy.py").write_text("x = 1\n") + (repo / "calm.py").write_text("y = 2\n") + subprocess.run(["git", "add", "-A"], cwd=repo, check=True) + subprocess.run(["git", "commit", "-qm", "feat: initial"], cwd=repo, check=True) + (repo / "buggy.py").write_text("x = 2\n") + subprocess.run(["git", "add", "-A"], cwd=repo, check=True) + subprocess.run(["git", "commit", "-qm", "fix(core): off-by-one"], cwd=repo, check=True) + + ranked = rank(repo, {"files": {"calm.py": {"summary": {"missing_lines": 9999}}}}) + assert ranked, ranked + assert ranked[0]["path"] == "buggy.py", ranked + assert ranked[0]["tier1_escaped_defects"] == 1.0, ranked[0] + # calm.py has 9,999 uncovered statements and still loses to one fix commit. + calm = [r for r in ranked if r["path"] == "calm.py"] + assert calm and calm[0]["rank_key"] < ranked[0]["rank_key"], ranked + + # A hollow history sinks the top candidate below the bulk file. + sunk = rank( + repo, + {"files": {"calm.py": {"summary": {"missing_lines": 9999}}}}, + hollow_rates={"buggy.py": 1.0}, + ) + assert sunk[0]["path"] == "calm.py", sunk + + # --- an unreadable coverage report must not read as "everything is covered" ------------- + # Break -> revert 2026-08-26: restoring the silent `if path.exists()` fall-through makes the + # CLI report a column of zeros for tier 3 with no indication that nothing was read. + import contextlib + import io + + with tempfile.TemporaryDirectory() as td: + repo = Path(td) + subprocess.run(["git", "init", "-q", "."], cwd=repo, check=True) + subprocess.run(["git", "config", "user.email", "a@b.c"], cwd=repo, check=True) + subprocess.run(["git", "config", "user.name", "t"], cwd=repo, check=True) + (repo / "m.py").write_text("z = 1\n") + subprocess.run(["git", "add", "-A"], cwd=repo, check=True) + subprocess.run(["git", "commit", "-qm", "fix: something"], cwd=repo, check=True) + buf = io.StringIO() + with contextlib.redirect_stdout(buf): + main(["--repo", str(repo), "--coverage-json", str(repo / "nope.json")]) + out = buf.getvalue() + assert "UNAVAILABLE" in out, out + assert "does not exist" in out, out + + # --- the Brain status never guesses ------------------------------------------------------ + absent = brain_signal_status("/no/such/store.db") + assert absent["source"] == "git" and absent["brain"] == "unknown", absent + + print( + "escaped_defect_priority.py selftest: OK (tier ordering, hollow multiplier, fix-subject " + "matching, contamination drop, live git ranking, and an unreadable Brain reported as " + "unknown rather than zero)" + ) + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__.split("\n")[0]) + parser.add_argument("--selftest", action="store_true") + parser.add_argument("--repo", default=".", help="repository to rank") + parser.add_argument("--coverage-json", type=Path, default=None, help="coverage.py JSON report") + parser.add_argument("--lookback-days", type=int, default=DEFAULT_LOOKBACK_DAYS) + parser.add_argument("--limit", type=int, default=DEFAULT_LIMIT) + parser.add_argument("--json", action="store_true") + ns = parser.parse_args(argv) + if ns.selftest: + _selftest() + return 0 + + # A MISSING COVERAGE FILE IS NOT AN EMPTY ONE. The first version of this silently fell + # through to {} when the path did not exist, so tier 3 rendered as a column of zeros that + # looked like "everything is covered" rather than "nothing was read" -- the same + # could-not-measure-as-measured-zero defect this module's own tier 1 note describes. Caught + # when a scratch path was cleaned up between runs and the ranking cheerfully carried on. + cov: dict[str, Any] = {} + tier3 = "off (no --coverage-json given)" + if ns.coverage_json: + if not ns.coverage_json.exists(): + tier3 = f"UNAVAILABLE — {ns.coverage_json} does not exist" + else: + try: + cov = json.loads(ns.coverage_json.read_text(encoding="utf-8")) + n = len(uncovered_by_file(cov)) + tier3 = ( + f"{n} file(s) with missing statements" + if n + else f"UNAVAILABLE — {ns.coverage_json} parsed but names no in-repo file " + "with missing statements (wrong report, or wrong path root?)" + ) + except (OSError, json.JSONDecodeError) as exc: + tier3 = f"UNAVAILABLE — {ns.coverage_json} unreadable: {exc}" + ranked = rank(ns.repo, cov, lookback_days=ns.lookback_days, limit=ns.limit) + status = brain_signal_status() + if ns.json: + print( + json.dumps( + {"tier1_source": status, "tier3_coverage": tier3, "ranked": ranked}, indent=2 + ) + ) + return 0 + print(f"tier 1 source: {status['source']} ({status.get('reason')})") + print(f"tier 3 coverage: {tier3}") + print(f"{'':<3}{'FILE':<52}{'DEFECT':>7}{'CHURN':>7}{'UNCOV':>7}") + for i, row in enumerate(ranked, 1): + print( + f"{i:<3}{row['path'][:50]:<52}{row['tier1_escaped_defects']:>7}" + f"{row['tier2_churn']:>7}{row['tier3_uncovered_statements']:>7}" + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/test_escaped_defect_priority.py b/tests/test_escaped_defect_priority.py new file mode 100644 index 0000000..511798e --- /dev/null +++ b/tests/test_escaped_defect_priority.py @@ -0,0 +1,191 @@ +"""The ranking's invariants, pinned where the hollow detector can grade them. + +These are pytest tests rather than selftest cases on purpose, and the reason is the subject of the +module they test: `local_verify` grades per pytest NODE, so a selftest — one exit code — is a +single node and its internal assertions are invisible to hollow-test detection. A module whose job +is to order test-writing work should have its own tests gradeable by the gate that judges the work +it orders. + +The module keeps a `--selftest` too. The two are complementary: the selftest exercises the CLI the +way it ships, including live `git log` parsing against a real temporary repository; these pin the +ordering rules as pure functions, where a break is attributable to one invariant. +""" + +from __future__ import annotations + +import json +import subprocess +from pathlib import Path + +import escaped_defect_priority as edp + + +def _repo(tmp_path: Path, commits: list[tuple[str, dict[str, str]]]) -> Path: + """Build a real git repo. Real, not mocked: the parsing is the part that breaks.""" + subprocess.run(["git", "init", "-q", "."], cwd=tmp_path, check=True) + subprocess.run(["git", "config", "user.email", "a@b.c"], cwd=tmp_path, check=True) + subprocess.run(["git", "config", "user.name", "t"], cwd=tmp_path, check=True) + for subject, files in commits: + for name, body in files.items(): + target = tmp_path / name + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(body, encoding="utf-8") + subprocess.run(["git", "add", "-A"], cwd=tmp_path, check=True) + subprocess.run(["git", "commit", "-qm", subject], cwd=tmp_path, check=True) + return tmp_path + + +# --------------------------------------------------------------------------------------------- +# Tier ordering. The whole point of the module is that the tiers do not trade off against each +# other on volume, so each of these asserts a STRICT ordering rather than a score value. +# --------------------------------------------------------------------------------------------- + + +def test_one_escaped_defect_outranks_any_amount_of_uncovered_code(): + """This is the design decision, stated as an assertion. + + Ranking by uncovered mass points agents at the largest files, which are the glue modules where + hollow tests get written. A file that actually needed a bug fix is evidence about the TESTS; + an uncovered count is only evidence about the code. + """ + defect = edp.FileScore(path="a.py", escaped=1.0) + # 10**9, not a modest number: the first implementation multiplied the tiers apart and summed + # them, which inverts once tier 3 reaches the multiplier. The tuple holds at any magnitude. + bulk = edp.FileScore(path="b.py", uncovered=10**9) + assert defect.sort_key() > bulk.sort_key() + + +def test_churn_outranks_uncovered_but_loses_to_a_defect(): + defect = edp.FileScore(path="a.py", escaped=1.0) + churny = edp.FileScore(path="b.py", churn=10**6) + bulk = edp.FileScore(path="c.py", uncovered=10**9) + assert defect.sort_key() > churny.sort_key() > bulk.sort_key() + + +def test_a_revert_counts_for_more_than_an_ordinary_fix(): + """Somebody judging a change wrong outright is stronger evidence than a follow-up fix.""" + assert edp.REVERT_WEIGHT > edp.FIX_WEIGHT + + +# --------------------------------------------------------------------------------------------- +# The hollow multiplier — the term that keeps this from becoming another metric to game. +# --------------------------------------------------------------------------------------------- + + +def test_a_fully_hollow_history_sinks_a_file_to_zero(): + """A module where every generated test passes against a broken base is worth no more work. + + Without this term the ranking would keep sending agents at the same untestable glue forever, + and each visit would raise coverage while proving nothing. + """ + hollow = edp.FileScore(path="a.py", escaped=99.0, churn=99, uncovered=99, hollow_rate=1.0) + assert hollow.sort_key() == (0.0, 0.0, 0.0) + + +def test_hollow_rate_scales_rather_than_switches(): + full = edp.FileScore(path="a.py", escaped=1.0) + half = edp.FileScore(path="a.py", escaped=1.0, hollow_rate=0.5) + assert half.sort_key()[0] == full.sort_key()[0] / 2 + + +def test_an_unmeasured_file_is_not_treated_as_a_good_one(tmp_path): + """No hollow history means no penalty — and that is deliberate, not an oversight. + + Unmeasured is not the same as measured-good, but the first attempt on a file is what produces + the measurement, so a file nobody has tried must stay reachable. The penalty applies once + there is evidence, never before. + """ + repo = _repo(tmp_path, [("fix: boom", {"m.py": "x = 1\n"})]) + ranked = edp.rank(repo, {}) + assert ranked[0]["path"] == "m.py" + assert ranked[0]["hollow_rate"] == 0.0 + + +# --------------------------------------------------------------------------------------------- +# Could-not-measure is never measured-zero. The module's own first draft got this wrong: a +# missing coverage report fell through to an empty dict and tier 3 rendered as a column of zeros, +# which reads as "everything is covered" rather than "nothing was read". +# --------------------------------------------------------------------------------------------- + + +def test_absolute_paths_are_dropped_as_contamination(): + """A tmp-workspace copy is not a file anyone can open, so it must not be ranked. + + That contamination put 95 phantom rows into a real Gate payload and filled 13 of its 15 + worst-file slots, which is how a coverage report came to point at files that do not exist. + """ + cov = { + "files": { + "src/real.py": {"summary": {"missing_lines": 10}}, + "/tmp/pytest-of-runner/ws/src/real.py": {"summary": {"missing_lines": 900}}, + } + } + assert edp.uncovered_by_file(cov) == {"src/real.py": 10} + + +def test_a_missing_coverage_report_is_reported_not_silently_empty(tmp_path, capsys): + repo = _repo(tmp_path, [("fix: boom", {"m.py": "x = 1\n"})]) + edp.main(["--repo", str(repo), "--coverage-json", str(tmp_path / "absent.json")]) + out = capsys.readouterr().out + assert "UNAVAILABLE" in out + assert "does not exist" in out + + +def test_an_unparseable_coverage_report_names_the_failure(tmp_path, capsys): + repo = _repo(tmp_path, [("fix: boom", {"m.py": "x = 1\n"})]) + bad = tmp_path / "bad.json" + bad.write_text("{not json", encoding="utf-8") + edp.main(["--repo", str(repo), "--coverage-json", str(bad)]) + out = capsys.readouterr().out + assert "UNAVAILABLE" in out and "unreadable" in out + + +def test_a_brain_that_cannot_be_read_reports_unknown_not_zero(): + """`unknown` and `no escaped defects` are opposite findings; only one is good news.""" + status = edp.brain_signal_status("/no/such/store.db") + assert status["brain"] == "unknown" + assert status["source"] == "git" + + +# --------------------------------------------------------------------------------------------- +# Commit-subject matching, against the forms real history actually uses. +# --------------------------------------------------------------------------------------------- + + +def test_fix_subjects_match_and_prose_about_fixing_does_not(): + assert edp.FIX_SUBJECT.search("fix(coverage): omit tmp rows") + assert edp.FIX_SUBJECT.search("fix: null deref") + assert edp.FIX_SUBJECT.search("Fixed the crash") + # The anchor is what stops a feature commit that merely mentions fixing from scoring. + assert not edp.FIX_SUBJECT.search("feat: prefix the fix with a scope") + assert not edp.FIX_SUBJECT.search("docs: explain how to fix it") + + +def test_ranking_ignores_non_python_and_test_files(tmp_path): + """Churn on a lockfile is true and useless; a test file is not a testgen target.""" + repo = _repo( + tmp_path, + [ + ( + "fix: touch several things", + { + "m.py": "x = 1\n", + "requirements.lock": "pkg==1\n", + "tests/test_m.py": "def test_x():\n assert True\n", + }, + ) + ], + ) + paths = [r["path"] for r in edp.rank(repo, {})] + assert "m.py" in paths + assert "requirements.lock" not in paths + assert "tests/test_m.py" not in paths + + +def test_json_output_carries_both_source_declarations(tmp_path, capsys): + """A consumer must be able to tell which tier-1 source produced the order it is acting on.""" + repo = _repo(tmp_path, [("fix: boom", {"m.py": "x = 1\n"})]) + edp.main(["--repo", str(repo), "--json"]) + payload = json.loads(capsys.readouterr().out) + assert "tier1_source" in payload and "tier3_coverage" in payload + assert payload["tier1_source"]["source"] in {"git", "brain"} From 4d1600dd14f79d6842d22d015ac1b36ae8feb71c Mon Sep 17 00:00:00 2001 From: Tim Stranske Date: Sat, 29 Aug 2026 13:46:24 -0500 Subject: [PATCH 2/2] chore(floor): resolve against #152 as the union, re-measured on the merge result #152 landed underneath this branch and added a NEW floor key, mirror_skipped_max, bounding the exec mirror by its own skip ceiling rather than the runner's. Resolved as the UNION per the rule this file states: main's structure kept in full including that key, this branch's rationale appended rather than either note replacing the other, and the count RE-MEASURED on the merge result rather than carried forward from either side. 515 from verify.py's own run after the rebase, which confirms #152 added no collected tests. That makes 502 + 13 correct -- but measured, not assumed. Taking a side would have been silently right this time, and it is what put the floor 8 below reality on an earlier occasion. Verified on the merge result: 515 passed, 0 failed, 0 skipped, 88/88 selftests, 5/5 gates. Co-Authored-By: Claude Opus 5 --- .verify-floor.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.verify-floor.json b/.verify-floor.json index f51d834..66219b6 100644 --- a/.verify-floor.json +++ b/.verify-floor.json @@ -1,10 +1,10 @@ { - "collected": 502, - "passed": 502, + "collected": 515, + "passed": 515, "skipped_max": 26, "mirror_skipped_max": 31, "selftest_skipped_max": 7, "gate_skipped_max": 2, "mypy_exempt_max": 0, - "note": "Recorded by verify.py --update-floor, except the *_max ceilings, which are edited BY HAND and never re-measured. `collected` catches tests that stopped being collected; `passed` is compared against passed+skipped, so a check may move between passing and consciously-skipped but the two together may never shrink. The *_max ceilings bound the skipped side: 24/7/2 is exactly what a machine with none of this instance's local prerequisites skips (a GitHub runner: no agent CLIs, no ~/.codex/skills, no /Applications/ChatGPT.app, no populated capability ledger), measured 2026-08-21. On the owner's machine all prerequisites exist and nothing skips at all. Raising a ceiling is a deliberate act: it means agreeing that one more thing is allowed to go unchecked, so say which and why in the commit. LOWERED 26 -> 24 on 2026-08-22, reverting the raise made earlier the same day. The two kill-switch exemption tests no longer need to skip on a bare runner: their declarations moved out of the running instance's ledger and into capabilities.KNOWN_DECLARATIONS, so they assert code-derived truth and run everywhere. Moving a test back below the ceiling is the preferred way to lower it -- fix what made it machine-dependent, rather than agreeing to check less. FLOOR 345 -> 353 on 2026-08-22: 345 was measured on a branch cut before #13 (research panels/rounds/domain studies) merged, so the recorded floor sat 8 tests BELOW what main actually collects. A floor below reality is the permissive direction -- those 8 could have silently stopped being collected and still cleared the check, which is exactly the hole this file exists to close. Measure the floor on the merge result, not on the branch. Raised again on 2026-08-22 by the producer-identity-scope branch, which adds tests on top of the 353 recorded by #15; re-measured after rebasing rather than assumed. NOTE: `verify.py --update-floor` REPLACES this note with a generic one, so it must be restored by hand after every use \u2014 the ceiling rationale is the only record of which prerequisite justifies each skip. FLOOR 365 -> 366 on 2026-08-22 (heartbeat-ordering work, PR #18): exactly one new test, test_capabilities.test_no_tick_producer_runs_above_the_heartbeat_export. No ceiling moved and nothing new is skipped -- it reads source files rather than a populated ledger, so it runs on any machine. The branch recorded 354 because it was cut before #16 merged; re-measured on the MERGE RESULT per the rule above, which is exactly the mistake that put the floor 8 below reality last time. FLOOR 366 -> 368 on 2026-08-23: main collected 368 while this file recorded 366, drift left by #34 (evidence-acquisition landed, +1) and #37 (tick capability evidence, +1) whose authors each measured against a branch cut before the other merged. A floor BELOW reality is the permissive direction this file exists to close -- those two could have silently stopped being collected and still cleared the check. Measured on the merge result per the rule above: 368 passed, 0 failed, 0 skipped, 83/83 selftests, 43/43 can-fire, 5/5 gates. CEILING 24 -> 26 and FLOOR 368 -> 387 on 2026-08-23 (profiles/provenance branch, PR #42). This file CONFLICTED with #50, which raised the floor 366 -> 368 on main while this branch raised it to 387; resolved as the UNION rather than by taking a side -- #50's rationale is retained above and the count was RE-MEASURED on the new merge result instead of keeping either number. 368 (main) + 19 (this branch's net new tests) = 387; #50 corrected recorded drift rather than adding coverage, which is why 387 is unchanged from the pre-conflict measurement. Measured in a runner sandbox reproducing CI exactly (361 passed, 26 skipped, 387 collected) AND on the owner's machine (387 passed, 0 skipped, 5/5 gates). The two new skips are drift detectors against a REAL installed agent runtime, so neither can be moved below the ceiling -- the preferred way to lower one: (1) agy advertised-models cache absent, since comparing declared model ids against the catalogue agy actually advertises needs that catalogue, and a fixture would exercise the comparison while detecting no real drift; (2) vibe config absent (~/.vibe/config.toml), since active_model cannot be read to check for drift when there is no config to read. Both name their missing prerequisite, so a green run still states what it did not check. A third candidate skip was REFUSED: dispatcher's per-run agy-log assertion failed on a bare runner because adapters.advertised_models shells out to `agy models` when its disk cache is cold, and that probe landed inside a monkeypatched subprocess.run and overwrote the captured command. That is a stub leak, so it was fixed by ISOLATING the double rather than by skipping -- which makes CI run MORE. FLOOR 387 -> 391 on 2026-08-23 (improvement-log accessor, PR #59): exactly four new tests, all in test_improvement_log.py -- three read tracked files in the tree (the pointer's size and content, and that CLAUDE.md 0 step 3 and 5 name the accessor rather than a bare path) and one runs the accessor as a subprocess against a path that cannot exist. None reads a populated ledger, an agent CLI or ~/.codex, so all four RUN on a bare runner and NO ceiling moved: nothing new is skipped. Measured on the MERGE RESULT after rebasing onto origin/main af6654d, which collected 387 -- not on the branch base, per the rule above. FLOOR 391 -> 402 on 2026-08-23 (Gate python-ci configuration, the PR that adds the missing .github/workflows/autofix-versions.env): exactly 11 new tests, all in test_ci_gate_config.py, which read committed files only -- the pin file, ruff.toml, mypy.ini, pr-00-gate.yml's toggle annotations and docs/CI_LINT_BASELINE.md. NO ceiling moved. On any CHECKOUT -- CI, the owner's tree, a second instance -- all 11 run: they need no installed linter and no populated ledger. In the EXEC-MIRROR layout all 11 skip with one named reason, because orch-sync-mirror.sh copies root-level *.py only, so .github/workflows, docs/ and scripts/ are genuinely absent there (env_prereq.repo_files_absent). That lands at 11/26 on a machine that otherwise skips nothing, and CI stays at 26/26, so no ceiling needed raising. The skip gate is the presence of those DIRECTORIES, never of the pin file itself -- gating on the file would have made the test that checks for it unable to fail. Measured on the merge result, twice: the branch was rebuilt on origin/main after #42 and #59 merged, and re-measured after #61 merged and was merged in -- 393 passed + 9 skipped = 402 collected both times, so #61 added no collected tests and this floor is not sitting below reality. #61 itself left main's floor at 391, which is exactly main-without-these-11, so there is no inherited drift to correct. RULE CHANGE 2026-08-23: `collected` is now an EQUALITY, not a minimum. Every floor entry above this one records the number being found BELOW reality and hand-raised after the fact -- 21 low at the worst, then 8, then 1, then 2 -- because nothing ever required a test-adding PR to touch this file, so the permissive direction was silent by construction and the rule 'measure on the merge result' had to be restated three times with nothing enforcing it. verify.py now FAILS when collected exceeds the floor, printing the two integers to write. That also makes the concurrency case self-enforcing: once every test-adding branch must edit these same two lines, two concurrent branches CONFLICT IN GIT, so the second cannot merge without rebasing onto the first and re-measuring on the actual merge result. Demonstrated repeatedly on the change itself: six merges landed on main in the two hours it took to write, moving this file 368 -> 387 -> 391 -> 402, and every one would have left the floor below reality under the old one-directional rule. `passed` deliberately stays a MINIMUM on passed+skipped: only collection is machine-invariant (a skipped test is still collected), measured across machines at 391 collected on both, with pass/skip splits of 365/26 on CI against 391/0 locally. The *_max ceilings are untouched by this change and nothing new is skipped. `--update-floor` also stops REPLACING this note -- it appends -- so the warning above about restoring it by hand no longer applies; and drift does NOT block --update-floor, since a gate that forbade its own only remedy would be a deadlock (the first draft was exactly that). FLOOR 402 -> 407 on 2026-08-23 (findability admission requirement). (findability admission requirement). (findability admission requirement). (findability admission requirement). Exactly five new pytest tests, all in test_capability_admission.py: test_findability_distinguishes_its_three_sub_causes, test_findability_blocks_new_capabilities_and_reports_older_ones_as_debt, test_unreadable_reach_is_not_evaluated_and_never_a_failure, test_findability_exemption_is_declared_in_code_not_in_a_live_ledger, test_consult_sites_are_falsifiable_claims_about_real_callers. NO CEILING MOVED and nothing new skips: all five build synthetic ledgers in a tempdir or read committed tables, so none needs a populated capability ledger, an agent CLI or ~/.claude/skills. The one machine-dependent thing they touch -- an external consult site declared in capability_advisor.CONSULT_SITES whose skill prompt is not on this machine -- is reported as UNVERIFIED rather than skipped, because absence of the caller is not refutation of the claim; the in-tree site (tick) is asserted verified on every machine so the check can never degrade into 'everything unverified, nothing tested'. Measured on the merge result per the rule above: this file CONFLICTED three times while the branch was open, as main went 387 -> 391 -> 402 (#61, #64, #65, #60). Each time it was resolved as the UNION rather than by taking a side, and the count was RE-MEASURED on the new merge result rather than either number being carried forward: 402 (main at bd6da2e) + 5 (this branch's new tests) = 407. That is the rule this file already states -- measure the floor on the merge result, not on the branch -- and it mattered here, because #60 both deleted test_ci_gate_config.py and added more than it removed, so guessing in either direction would have been wrong. -> re-measured on 2026-08-23 (PR #62, the four deferred #42 review findings): three new tests, all machine-independent (each builds its own tmp_path Brain and manifests), so NO CEILING MOVED and nothing new is skipped. Fourth conflict for this branch, and the first one under the EQUALITY -- which is the point: the equality's own rationale says git conflict detection is what enforces 'measure on the merge result', and that is exactly what happened here. Under the old minimum the three earlier conflicts could each have been resolved by keeping the larger number; under the equality the count MUST be measured, and it was. RESOLVED AGAINST #68 (findability admission requirement) on 2026-08-23: taken as the UNION per the rule this file states -- #68's five-test entry is retained above and this branch's three-test entry beside it -- and the count RE-MEASURED on the merge result rather than keeping either side's number. main fc1fd42 collects 407; this branch adds 3; 410 measured with `pytest --collect-only -q` on the merge result, not assumed. Ceilings untouched at 26/7/2 and nothing new is skipped. Also resolved in the same merge: langsmith-fleet-worker-attempt.json, a CI-emitted `langsmith-fleet/v1` worker-attempt record whose two sides differed only in `emitted_at` and `pr_number` (62 here, 68 on main). Main's NEWER record was kept rather than this branch's older one -- discarding a newer provenance observation to win a merge would corrupt exactly the causal-provenance evidence CLAUDE.md 2 protects, and this branch's own run re-emits its record anyway. FLOOR 410 -> 411 on 2026-08-23 (CodeRabbit follow-up on PR #42, thread 3837879039; re-measured again after #56 made `collected` an EQUALITY, which makes an assumed number a hard RED rather than a quiet pass -- main stayed at 402 across #56, and the merge result measures 403, so #56 added no collected tests and this is main's 402 plus this branch's one): exactly one new test, test_feedback_model_provenance.test_late_sweep_completes_terminal_attempts_never_one_in_flight, which pins that ledger_reconcile.resolve_unresolved_worker_attempts completes only TERMINAL unresolved worker attempts and never one still in flight. No ceiling moved and nothing new is skipped -- the test builds its own tmp ledger and codex rollout fixture and monkeypatches adapters.CODEX_SESSIONS, so it needs no agent CLI and no populated capability ledger and runs on a bare runner. RESOLVED AGAINST #59 (improvement-log accessor), which raised the floor 387 -> 391 on main while this branch raised it to 388: taken as the UNION -- #59's rationale is retained above and the count was RE-MEASURED on the new merge result rather than keeping either number, which is the rule this file states and the mistake that once put the floor 8 below reality. 391 (main, incl. #59's four tests) + 1 (this branch's one new test) = 392 measured, not assumed: 392 passed, 0 failed, 0 skipped, 83/83 selftests, 43/43 can-fire, 5/5 gates. Three sibling follow-up branches are in flight against this same main (CI/ruff config, arm-attribution + durability, adapters label->ID); if this file conflicts with one of them, resolve as the UNION and RE-MEASURE on the new merge result rather than taking either number -- that is what #42 and #50 did, and taking a side is what put the floor 8 below reality earlier. RESOLVED AGAINST #68 (findability admission requirement) on 2026-08-23: taken as the UNION per the rule this file states -- #68's five-test entry is retained above and this branch's one-test entry beside it -- and the count RE-MEASURED on the merge result. main fc1fd42 collects 407; this branch adds 1; 408 measured with `pytest --collect-only -q` on the merge result, not assumed. Ceilings untouched at 26/7/2 and nothing new is skipped -- the one new test builds its own tmp ledger and codex rollout fixture, so it runs on a bare runner. Also resolved in the same merge: langsmith-fleet-worker-attempt.json, a CI-emitted `langsmith-fleet/v1` worker-attempt record differing only in `emitted_at` and `pr_number`; main's NEWER record was kept, since discarding a newer provenance observation to win a merge would corrupt the causal-provenance evidence CLAUDE.md 2 protects. FLOOR 411 -> 415 on 2026-08-23 (PR #70 diagnostics salvage): four new collected tests in test_capability_set_coverage.py from the PR #43 salvage plus CodeRabbit follow-ups on PR #51/#70 \u2014 union/missing-candidate fetch command, truncation after six modules, AST-scoped gate-call audit, and entrypoint-diagnosis coverage. NO CEILING MOVED and nothing new is skipped; all inject synthetic ledgers or read committed source. Measured on the merge result at 91d37fa: 389 passed + 26 skipped = 415 collected on CI, not assumed. FLOOR 415 -> 416 on 2026-08-23 (the dangling-citation follow-up, PR #74): exactly ONE new test, test_ci_gate_config.test_every_cited_repo_path_resolves, which reads the two committed config files this repo OWNS (the pin file and ruff.toml) and asserts every repo-relative path they cite exists. It exists because the pin file shipped citing docs/ci/LINT_BASELINE.md when the real path was docs/CI_LINT_BASELINE.md: the sibling checks read that file's CONTENTS thoroughly and its PROSE not at all, and the prose is the only pointer telling a reader where to re-measure before bumping a pin. Scoped to the two owned files deliberately -- scanning pr-00-gate.yml yields six findings that are all correct as written (guarded by hashFiles or a .agents check, or upstream paths), and a test that cries wolf gets waived. NO ceiling moved. RE-MEASURED SIX TIMES as the base moved under this ONE-LINE change: bd6da2e 402 -> ddb0928 402 -> fc1fd42 407 -> 0d661e3 407 -> 0593eeb 411 -> 6fed4ad 415, each +1 with this test, and the branch was rebuilt on each rather than the number carried forward. THIS BRANCH IS THE WORKED EXAMPLE of the equality's concurrency cost, so record it rather than rediscover it: main moved EIGHT times in the ~2.5 hours a one-line comment fix was open (#56, #68, #73, #69, #62, #70 and two direct commits), the floor line conflicted THREE separate times, and two merges overlapped the change directly -- #73 landed a byte-identical copy of the backplane-conformance.yml guard this branch also carried (dropped as redundant), and #69 edited this very test file in a neighbouring region. The equality is still the right call and should stay: every entry above this one records the floor being found BELOW reality, which is the permissive direction. But no amount of author care wins this race, because the correct value is only knowable on the merge result. The durable fix is CI running `verify.py --update-floor` on the merge commit, which keeps the equality and removes the race; until then a test-adding PR must be merged promptly after going green, because it re-conflicts on roughly every subsequent merge. FLOOR 416 -> 427 on 2026-08-23 (PR #72 hygiene untrack, rebased after #71 merged): exactly 11 new tests from test_repo_artifact_hygiene.py with root-anchored gitignore patterns. NO ceiling moved. Measured on merge result after #71 landed on main: 416 (main) + 11 = 427 collected via pytest --collect-only -q, not assumed. #71's simpler untrack landed first; this branch carries the full hygiene test suite and corrected root-anchored patterns. FLOOR 427 -> 428 on 2026-08-23 (PR salvaging #34/#42 remnants): exactly one new test, test_feedback_model_provenance.test_gemini_provenance_reads_the_per_run_log_before_the_conversation_store, recovered from #42's post-merge commit 4e0d6ae along with the adapters catalog work it exercises. No ceiling moved and nothing new is skipped -- it seeds adapters._ADVERTISED_MEMO instead of letting the catalog probe shell out, so it runs on any machine and adds no prerequisite. `passed` is 428 rather than the 426 verify.py suggested on this machine: two test_capabilities liveness tests (test_gate_blocks_execution_is_opt_in_and_narrow, test_evidence_gate_kind_is_not_blanket_observer) currently fail HERE on pristine main as well, because the hourly fleet tick mutated the machine-local ledger and range-lane-rollout now classifies matched_not_invoked rather than deliberately_gated. That is ledger STATE, not this branch and not the code -- CI bootstraps an empty ledger and counts 428/428. Recording 426 would have baked a local environment failure into the floor as though it were the expected result. FLOOR 428 -> 441 on 2026-08-23 (coverage measures what actually runs): exactly 12 new tests, all in test_verify_coverage_mode.py. They read committed files and verify.py's own source, and monkeypatch verify.COVERAGE in-process -- no populated ledger, no agent CLI, no ~/.codex, and no coverage RUN -- so all 12 execute on any machine and NO ceiling moved. The change itself is a measurement fix, not a gate: `verify.py --coverage` wraps each child in `coverage run --parallel-mode` and combines, because the per-module --selftest is a SUBPROCESS and a pytest-only coverage run cannot see it. That blind spot was most of the codebase -- 78 modules have no test_*.py at all, ~85,500 lines, 79.6% of non-test root Python -- so the reported 48.45% was measuring the gap in the instrument, not a gap in the tests. Combined: 76.1% (45,049 statements, 10,774 missed). Twelve of the twelve modules the old report named as worst were selftest-only; outcomes.py reported 9.0% and measures 61.9%, watch.py 9.6% -> 86.9%. Coverage is OFF by default and deliberately never touches the exit code -- one of the 12 tests pins that, because enforcing a threshold here would reward pytest wrappers around already-tested modules: metric up, assurance flat. Measured on the MERGE RESULT: branched from origin/main bcc68cd (floor 427), then REBASED onto f5f1c39 when it landed underneath and re-measured on the new merge result rather than carrying the old number: 428 + 13 = 441. +1 on 2026-08-23 (440 -> 441 after the rebase), same branch: test_the_cli_help_actually_renders. It exists because this branch BROKE `verify.py --help` and its own twelve tests did not notice. argparse interpolates help strings with `% params`, so the literal `~80%` in the --coverage help was read as an `%o` octal conversion and --help died with 'badly formed help string'. All twelve original tests passed: every one inspected source text or monkeypatched a flag, and not one RENDERED the help -- a construction-time test suite that never exercised the constructed thing, which is this repo's founding defect one layer up. CI's verify.py gate caught it, which is the check of last resort working as intended. The new test runs `verify.py --help` as a subprocess and asserts it exits 0, so it runs anywhere and NO ceiling moved. Rendering rather than grepping for `%` is deliberate: a grep would flag the legitimate `%(default)s`. FLOOR 441 -> 442 on 2026-08-23 (matched_not_invoked yields to observers and declared gates): exactly one new test, test_capabilities.test_matched_not_invoked_yields_to_observers_and_declared_gates. It is SYNTHETIC on purpose and that is the point of it: the two tests that caught this bug in the wild read the LIVE ledger, so they skipped with a named reason on the empty ledger ci.yml bootstraps -- the defect was red on every populated machine and green on CI for as long as it existed. A synthetic row asks the same question everywhere, so this one RUNS on a bare runner and NO ceiling moved. The fix itself moves matched_not_invoked below `observing` and below the DECLARED deliberately_gated check in classify_liveness: it was the first check, which made it the fourth instance of the unescapable label the comments in that function exist to fix. Audited before committing -- 12 of 43 live rows reclassify (ten observers to observing, two declared gates to deliberately_gated) and ZERO move for any other reason, so nothing is reclassified by inference and the weaker gate_reason-only branch is untouched. Measured on origin/main 0d9c3a7, whose recorded floor is 441, so 441 + 1 = 442. FLOOR RECORDED by verify.py --update-floor on 2026-08-23: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. FLOOR RECORDED by verify.py --update-floor on 2026-08-24: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. FLOOR RECORDED by verify.py --update-floor on 2026-08-24: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. MYPY_EXEMPT_MAX introduced at 64 on 2026-08-23, the same change that flipped `typecheck` ON in pr-00-gate.yml. It was OFF because 608 whole-tree errors were drainable 0 per PR -- a gate whose clear path is blocked by the thing it measures. Two real changes opened it: the src/ move scoped the Gate's `target=src` to the 99 modules (608 -> 467), and pyproject.toml's [[tool.mypy.overrides]] exempts the modules that still have findings BY NAME so the 35 already-clean ones are checked today. 12 var-annotated findings were then drained (467 -> 455, 66 -> 64 modules) to prove the drain works rather than promise it. This ceiling may only ever be LOWERED, by typing a module and deleting its line -- raising it means agreeing one more module goes unchecked, so say which and why. NOT a suppression: no error code is disabled anywhere and the 455 stay visible via `python3 scripts/ci_lint_baseline.py`. Edited BY HAND like the other *_max values; --update-floor never re-measures it. MYPY_EXEMPT_MAX LOWERED 64 -> 43 on 2026-08-24, the ratchet's first real drain: 21 modules typed clean and removed from the exempt list, 455 -> 430 findings. Targeted the modules with 1-3 errors on purpose -- fixing 50 errors spread across the big modules would move this number by ZERO, and this number is what the gate reads. Lowering it is the drain; it may never be raised without naming the module and why. MYPY_EXEMPT_MAX LOWERED 43 -> 33 on 2026-08-24 (batch 2 of the drain): the whole <=5-finding tail typed clean. 66 of 99 modules now checked. What remains is five per-module campaigns (capability_advisor, dispatcher, capability_propensity, runtime_ac_gate, capabilities hold most of it), so future batches take ONE big module at a time rather than skimming. (batch 2 detail: 43 -> 26, 430 -> 374 findings, 73 of 99 modules checked. `mypy_path` gained `tests` so mypy can RESOLVE the recurrence-fixture roster capability_admission legitimately imports; the target stays `src`, but mypy then follows into that one test file, whose two findings were fixed rather than configured around.) MYPY_EXEMPT_MAX LOWERED 26 -> 20 on 2026-08-24 (batch 3): 79 of 99 modules checked. FLOOR 442 -> 448 on 2026-08-24 (absent-check detector + its ratchet): SIX new tests in tests/test_checks_reported.py holding the frequency rule and the expected-check ratchet. NO ceiling moved -- they call pure functions and read one committed JSON file, so they run on any machine. The ratchet exists because dogfooding the detector caught it DISARMING ITSELF: while pr-00-gate.yml sat held, every merged PR merged without the Gate, so after twelve such merges the Gate's checks fell below the 75% frequency threshold, the expected set eroded 23 -> 14, and PR #91 was pronounced clean by the tool written to catch exactly that. config/expected-checks.json is the high-water mark, seeded from PRs #87/#89 whose Gate demonstrably ran, and it only comes down when somebody deletes a line. LOWERED to 16 on 2026-08-24 (batch 4, re-applied after merging main, which carried #94/#100's own floor work \u2014 main's note kept, only the bound re-set). LOWERED 16 -> 16 on 2026-08-24 (batch 5): 83 of 99 checked. LOWERED 16 -> 12 on 2026-08-24 (batch 5): 87 of 99 checked. Lesson recorded three times now: measure with the PROJECT run, never per-file \u2014 `mypy src/X.py` reports clean for modules the project run still flags. FLOOR 448 -> 450 on 2026-08-24 (giving the per-node deliberate-break finding its consumers): exactly two new pytest tests, both in test_synthesis_promotion.py -- test_a_passing_break_names_the_tautologies_it_carried and test_the_break_caveat_is_silent_on_clean_and_on_pre_per_node_evidence. NO CEILING MOVED and nothing new is skipped: the first builds its own git repo in tmp_path and runs the real verification path (git + pytest only, no populated ledger, no agent CLI, no ~/.codex), and the second is a pure unit on _break_caveat. The third piece of this change is a runtime_ac selftest case, which adds NO collected test -- runtime_ac has a --selftest and synthesis_promotion does not, which is the whole reason only one of the two needed pytest tests. Measured on the merge result: branched from origin/main 8549f84, whose recorded floor is 448, and re-checked that main had not moved before writing this. 448 + 2 = 450, from verify.py's own count (450 passed, 0 failed, 0 skipped, 85/85 selftests, 5/5 gates), not assumed. FLOOR 448 -> 451 on 2026-08-24 (offload guidance correction, PR #113): THREE new tests in test_repo_artifact_hygiene.py -- two parametrized entries adding src/UNKNOWN.egg-info/PKG-INFO and UNKNOWN.egg-info/PKG-INFO to EMITTED_ARTIFACTS, and test_no_build_metadata_is_tracked. NO ceiling moved and nothing new skips: all three shell out to git against this checkout, so they run anywhere git does. They exist because THIS PR carried four committed build artifacts and no check objected. A CI step runs `pip install -e .`, setuptools writes src/UNKNOWN.egg-info/ (named UNKNOWN because pyproject.toml declares no [project] on purpose), and the repo's own autofix bot committed all four -- .gitignore had no *.egg-info/ entry and this suite only knew the langsmith-fleet family. CodeRabbit caught it; nothing local did. Both locations are listed because a checkout builds into src/ while the EXEC MIRROR IS FLAT. The tracked test is separate from the ignored test on purpose: the four files were committed BEFORE the pattern existed, and adding a pattern does nothing to a path git already tracks -- so an ignore-only check passes on a repo still carrying the debris. RESOLVED AGAINST #110 and #114 on 2026-08-24, which moved main 448 -> 450 while this branch raised it to 451: taken as the UNION per the rule this file states -- main's entries are retained above and this branch's three-test entry beside them -- and the count RE-MEASURED on the merge result rather than either number being carried forward. 450 (main) + 3 (this branch) = 453, measured with `pytest --collect-only -q`, not assumed. Ceilings untouched and nothing new is skipped. FLOOR 453 -> 458 on 2026-08-24 (untracking .coverage, the coverage database this repo's own verify.py writes): exactly FIVE new tests, all in test_repo_artifact_hygiene.py -- two parametrized ignore cases over COVERAGE_DATA_FILES (.coverage and a parallel-mode .coverage...), one untracked case, and two parametrized must-stay-committable cases (.coveragerc, tools/coverage_guard.py). NO CEILING MOVED and nothing new is skipped: all five ask GIT about a path in this checkout (check-ignore / ls-files) and need no populated ledger, no agent CLI and no ~/.codex, so all five RUN on a bare runner. The change is `git rm --cached .coverage` plus root-anchored `/.coverage` and `/.coverage.*`; the 90 KB SQLite database arrived on main in #109, a typing PR whose every other file is about mypy, and verify.py's coverage_reset() unlinks it and coverage_combine_and_report() rewrites it on every --coverage run -- so while tracked it was an opaque binary rewritten by the command that produces this repo's verdict. Both patterns because two different steps write them, and `/.coverage.*` rather than `/.coverage*` because the second swallows .coveragerc. Break -> revert performed in all three directions and recorded in the test file. Measured on the MERGE RESULT: fast-forwarded onto origin/main 5c769e0, whose recorded floor is 453, and re-fetched to confirm main had not moved again before measuring. 453 + 5 = 458 from verify.py's own count (458 passed, 0 failed, 0 skipped, 85/85 selftests, 5/5 gates), not assumed. LOWERED 12 -> 10 on 2026-08-24 (batch 6): keepalive_outcomes (17 findings) and redirect_sweep (18) typed clean, 89 of 99 modules checked, 240 -> 205 findings. Both modules were drained by WRITE-MODE isolated offloads (dispatcher.offload --isolate) rather than in-seat, and every diff was reviewed here before it was applied -- the two changes that could have altered behaviour were checked first-person and both are equivalent: redirect_sweep's marker-rc read keeps the same except clause wrapping it, so a non-numeric rc still raises inside the try and still lands at None; keepalive_outcomes' added `oc is not None` guard short-circuits ahead of _should_record_outcome, whose own first line already returns False on oc is None. LOWERED 10 -> 7 on 2026-08-24 (batch 6b): dispatcher (65 findings), capability_propensity (47) and runtime_ac_gate (34) typed clean, 92 of 99 modules checked, 205 -> 91 findings. All three drained by write-mode isolated offloads and then INTEGRATED BY HAND rather than applied as returned -- three of the agents' choices were replaced with smaller ones, and the reasons are the durable part. (1) capability_propensity came back with two `type: ignore`s at the correlated-arm lookup, justified as an ambiguous key type. The ambiguity was real but the fix was in the wrong file: research_subjects.reciprocal_evidence_weights is GENERIC over its member type -- run-id strings at one caller, verdict indices at the other -- and its `dict[str, float]` return was too narrow. It is a TypeVar now and both ignores are gone. Suppressing there would have hidden a false positive AND blinded the one check that would catch a real key-type mismatch in the correlated-arm discount, which CLAUDE.md 2 makes load-bearing. (2) capability_propensity.detect() came back rewritten into four locals; behaviour-preserving (verified: same objects mutated, same key insertion order) but one `out: dict[str, Any]` annotation does the same job, so the annotation was taken instead. (3) runtime_ac_gate's spec_path came back with `spec_dir or env.get(K, DEFAULT)` split into branches, which silently falls back to DEFAULT where the original raised TypeError on Path(None). Unreachable via os.environ, but this is a GATE and 'silently use a default' is the permissive direction; with env typed as Mapping[str, Any] the original one-liner type-checks unchanged, so the restructure bought nothing. Asserts added in production paths were each checked against their invariant rather than trusted: dispatcher's `profile_attempt_id is not None` sits inside `elif selected_profile:` where line 815 makes it non-None by construction, and attach_profile_attempt_to_decision already declares `decision_id: str`. LOWERED 7 -> 1 on 2026-08-24 (batch 6c): capabilities (14 findings, ONE missing annotation on KNOWN_GATES caused all of them), router (16), and the last five findings across feedback/runtime_ac/capability_outcome_bridge/capability_compiler. 98 of 99 modules now checked; only capability_advisor (24) remains, held back because PR #113 edits the same file. The drain added ZERO net type: ignore -- router's one remaining suppression predates this work. THE DRAIN FOUND A REAL BUG and the value is in how it was handled: router's load_backlog promised a bare-list payload in its own docstring and could never read one, because `.get` on a list raises AttributeError into a broad `except` that returns [] -- so a populated backlog read as NO WORK. The offloaded agent was told to report suspected real bugs rather than fix them, did exactly that, and left a type: ignore documenting it; the fix landed here instead, with a selftest asserting a non-empty list survives (an empty one cannot distinguish the fix from the failure, since [] is also what a parse error returns) and a break->revert showing the old one-liner returning [] for a valid backlog. Latent, not live: the file on disk is the {\"items\": [...]} dict form, whose behaviour including its no-items fallback is unchanged. Worth closing anyway -- the day discovery writes the documented shape the symptom would be silence, which is this repo's signature failure. LOWERED 1 -> 0 on 2026-08-24 (batch 6d, THE LAST ONE): capability_advisor's 24 findings typed, the [[tool.mypy.overrides]] block removed entirely, and all 99 of 99 modules now checked. 240 findings drained to zero across the campaign with ZERO net `# type: ignore` added. THE LAST MODULE EXPOSED A LATCHED GATE IN THE RATCHET ITSELF, which is the durable finding here. verify.mypy_exempt_modules() returned None for BOTH 'pyproject unreadable' and 'readable, no override block' -- so the run that FINISHED the drain would have printed 'mypy ratchet: NOT COUNTED', the gate going silent at the exact moment it succeeded. Its own docstring already argued against this ('a ratchet that stops being counted is indistinguishable from one that emptied, and only one of those is good news'), and two more things prove it was unintended: _format_mypy_exempt_line carried a ' -- fully drained' branch no input could reach, and the selftest asserted the function was TRUTHY, so an empty list failed it. That is the CLAUDE.md pattern exactly -- a gate whose clear path is blocked by the thing it measures -- and it would have latched on the last module. Fixed: [] now means answered-and-empty, None stays unanswerable, and the selftest asserts all three renderings plus `is not None` rather than truthiness. `collected` is unchanged at 453: the new coverage lives in verify.py's own selftest, which pytest does not collect. FLOOR 500 -> 502 on 2026-08-25 by the capacity-arity branch: +2 tests, both pinning that `capacity.compute` returns THREE values on every branch. It used to return 2 or 3 depending on which of `_classify`'s 21 returns ran, and the first of those is the 429-shed check, whose `_shed` reads a file on the HOST outside `$ORCH_STATE_DIR` \u2014 so the ARITY was a property of the machine, and `test_capacity_gate_is_seat_level_not_gemini_special` died on the unpack locally while CI, where nothing is shed, stayed green. Measured on the merge result AFTER rebasing onto origin/main, which had taken #140 (500) mid-session: 502 on the rebase, not 460 on the pre-#140 branch, which is exactly the drift this equality exists to catch. MIRROR CEILING ADDED 2026-08-29: `mirror_skipped_max` = 31. `skipped_max` was ONE number bounding TWO different deprived environments, and this note above says which one it was measured in -- a GitHub runner with none of this instance's local prerequisites. The EXEC MIRROR is a different shape: every local prerequisite is present there (it exists only on the machine the system runs on, so nothing skips for a missing CLI, skill, app bundle or ledger row) but it is a FLAT FILE COPY, so it is not a git repository and has no .github/. Measured on unmodified main: 471 passed, 31 skipped -- 12 from `repo_files_absent('.github/workflows', ...)` in test_ci_gate_config.py and 19 from `git_repo_absent()` in test_repo_artifact_hygiene.py, and ZERO runner-shape skips. Against the 26 measured on a runner that skips neither family, `python3 verify.py` from ~/.codex/orchestrator-mirror was RED ON EVERY INPUT, including an entirely correct tree -- confirmed structural by syncing the PREVIOUS main into a scratch mirror and getting the same 31. CLAUDE.md 1 makes the mirror run the verdict ('cmp-clean is not agreement'), so the one instrument that catches cross-tree divergence had stopped protecting anything, and a gate red whatever you do is a gate that gets switched off. NOT FIXED BY RAISING `skipped_max` TO 31: CI runs on a runner where 26 is the correct bound, and the extra five would have let a runner skip five more checks in silence -- one gate going quiet to un-stick another. Instead each shape carries its own agreed number, selected by `env_prereq.exec_mirror_shape()`, which detects the PREREQUISITE (no .github/ AND git reports no repository) and never `$CI`, exactly as every other detector in that module does. Both marks are required because the mirror number is the LOOSER one, so an ambiguous tree falls back to the base. The mirror value REPLACES the base rather than adding to it: the runner-shape absences the base pays for cannot occur on the mirror, and 26+31 would have bought 26 units of headroom nothing there can legitimately spend. WHAT DRAINS IT: making a mirror-skipped test runnable there. Teaching orch-sync-mirror.sh to copy .github/ would drop 12 and this number must come down to 19 IN THE SAME CHANGE -- but note that script lives outside the repo, so a mirror synced by an unpatched copy would then be red at 31 > 19, which is the disease this entry cures; do it only if the script's state can be relied on. The 19 git-shape skips are irreducible while the mirror is a file copy. Any ceiling key may carry a `mirror_` variant by the same derived rule; only this one is set, and an unset variant falls back to the base agreement, which is the strict direction. FLOOR 502 -> 515 on 2026-08-26 (escaped-defect test priority): exactly THIRTEEN new tests, all in tests/test_escaped_defect_priority.py. NO CEILING MOVED and nothing new is skipped: each builds its own git repository in tmp_path or calls a pure function, so none needs a populated ledger, an agent CLI or ~/.codex, and all thirteen run on a bare runner. They are pytest rather than selftest cases ON PURPOSE, and the reason is the subject of the module: local_verify grades per pytest NODE, so a selftest is one node and its internal assertions are invisible to hollow-test detection -- a module that orders test-writing work should have its own tests gradeable by the gate that judges the work it orders. The module ALSO keeps a --selftest (88 of 88 now), which exercises the CLI the way it ships including live git log parsing; the two are complementary, not alternatives. Two of the thirteen caught a real defect in the module during review: the first implementation multiplied the tiers apart (1e6/1e3/1) and summed them, which holds only while the lower tiers stay small -- at 1,000,000 uncovered statements tier 3 exactly equals one escaped defect and the ordering the module exists to guarantee silently inverts. Replaced with a lexicographic tuple, which holds at any magnitude, and the tests now assert at 10**9 rather than a modest number. Measured on the MERGE RESULT: branched from origin/main b80e6e4, whose recorded floor is 502, and re-fetched to confirm main had not moved before measuring. 502 + 13 = 515 from verify.py's own count (515 passed, 0 failed, 0 skipped, 88/88 selftests, 5/5 gates), not assumed." + "note": "Recorded by verify.py --update-floor, except the *_max ceilings, which are edited BY HAND and never re-measured. `collected` catches tests that stopped being collected; `passed` is compared against passed+skipped, so a check may move between passing and consciously-skipped but the two together may never shrink. The *_max ceilings bound the skipped side: 24/7/2 is exactly what a machine with none of this instance's local prerequisites skips (a GitHub runner: no agent CLIs, no ~/.codex/skills, no /Applications/ChatGPT.app, no populated capability ledger), measured 2026-08-21. On the owner's machine all prerequisites exist and nothing skips at all. Raising a ceiling is a deliberate act: it means agreeing that one more thing is allowed to go unchecked, so say which and why in the commit. LOWERED 26 -> 24 on 2026-08-22, reverting the raise made earlier the same day. The two kill-switch exemption tests no longer need to skip on a bare runner: their declarations moved out of the running instance's ledger and into capabilities.KNOWN_DECLARATIONS, so they assert code-derived truth and run everywhere. Moving a test back below the ceiling is the preferred way to lower it -- fix what made it machine-dependent, rather than agreeing to check less. FLOOR 345 -> 353 on 2026-08-22: 345 was measured on a branch cut before #13 (research panels/rounds/domain studies) merged, so the recorded floor sat 8 tests BELOW what main actually collects. A floor below reality is the permissive direction -- those 8 could have silently stopped being collected and still cleared the check, which is exactly the hole this file exists to close. Measure the floor on the merge result, not on the branch. Raised again on 2026-08-22 by the producer-identity-scope branch, which adds tests on top of the 353 recorded by #15; re-measured after rebasing rather than assumed. NOTE: `verify.py --update-floor` REPLACES this note with a generic one, so it must be restored by hand after every use \u2014 the ceiling rationale is the only record of which prerequisite justifies each skip. FLOOR 365 -> 366 on 2026-08-22 (heartbeat-ordering work, PR #18): exactly one new test, test_capabilities.test_no_tick_producer_runs_above_the_heartbeat_export. No ceiling moved and nothing new is skipped -- it reads source files rather than a populated ledger, so it runs on any machine. The branch recorded 354 because it was cut before #16 merged; re-measured on the MERGE RESULT per the rule above, which is exactly the mistake that put the floor 8 below reality last time. FLOOR 366 -> 368 on 2026-08-23: main collected 368 while this file recorded 366, drift left by #34 (evidence-acquisition landed, +1) and #37 (tick capability evidence, +1) whose authors each measured against a branch cut before the other merged. A floor BELOW reality is the permissive direction this file exists to close -- those two could have silently stopped being collected and still cleared the check. Measured on the merge result per the rule above: 368 passed, 0 failed, 0 skipped, 83/83 selftests, 43/43 can-fire, 5/5 gates. CEILING 24 -> 26 and FLOOR 368 -> 387 on 2026-08-23 (profiles/provenance branch, PR #42). This file CONFLICTED with #50, which raised the floor 366 -> 368 on main while this branch raised it to 387; resolved as the UNION rather than by taking a side -- #50's rationale is retained above and the count was RE-MEASURED on the new merge result instead of keeping either number. 368 (main) + 19 (this branch's net new tests) = 387; #50 corrected recorded drift rather than adding coverage, which is why 387 is unchanged from the pre-conflict measurement. Measured in a runner sandbox reproducing CI exactly (361 passed, 26 skipped, 387 collected) AND on the owner's machine (387 passed, 0 skipped, 5/5 gates). The two new skips are drift detectors against a REAL installed agent runtime, so neither can be moved below the ceiling -- the preferred way to lower one: (1) agy advertised-models cache absent, since comparing declared model ids against the catalogue agy actually advertises needs that catalogue, and a fixture would exercise the comparison while detecting no real drift; (2) vibe config absent (~/.vibe/config.toml), since active_model cannot be read to check for drift when there is no config to read. Both name their missing prerequisite, so a green run still states what it did not check. A third candidate skip was REFUSED: dispatcher's per-run agy-log assertion failed on a bare runner because adapters.advertised_models shells out to `agy models` when its disk cache is cold, and that probe landed inside a monkeypatched subprocess.run and overwrote the captured command. That is a stub leak, so it was fixed by ISOLATING the double rather than by skipping -- which makes CI run MORE. FLOOR 387 -> 391 on 2026-08-23 (improvement-log accessor, PR #59): exactly four new tests, all in test_improvement_log.py -- three read tracked files in the tree (the pointer's size and content, and that CLAUDE.md 0 step 3 and 5 name the accessor rather than a bare path) and one runs the accessor as a subprocess against a path that cannot exist. None reads a populated ledger, an agent CLI or ~/.codex, so all four RUN on a bare runner and NO ceiling moved: nothing new is skipped. Measured on the MERGE RESULT after rebasing onto origin/main af6654d, which collected 387 -- not on the branch base, per the rule above. FLOOR 391 -> 402 on 2026-08-23 (Gate python-ci configuration, the PR that adds the missing .github/workflows/autofix-versions.env): exactly 11 new tests, all in test_ci_gate_config.py, which read committed files only -- the pin file, ruff.toml, mypy.ini, pr-00-gate.yml's toggle annotations and docs/CI_LINT_BASELINE.md. NO ceiling moved. On any CHECKOUT -- CI, the owner's tree, a second instance -- all 11 run: they need no installed linter and no populated ledger. In the EXEC-MIRROR layout all 11 skip with one named reason, because orch-sync-mirror.sh copies root-level *.py only, so .github/workflows, docs/ and scripts/ are genuinely absent there (env_prereq.repo_files_absent). That lands at 11/26 on a machine that otherwise skips nothing, and CI stays at 26/26, so no ceiling needed raising. The skip gate is the presence of those DIRECTORIES, never of the pin file itself -- gating on the file would have made the test that checks for it unable to fail. Measured on the merge result, twice: the branch was rebuilt on origin/main after #42 and #59 merged, and re-measured after #61 merged and was merged in -- 393 passed + 9 skipped = 402 collected both times, so #61 added no collected tests and this floor is not sitting below reality. #61 itself left main's floor at 391, which is exactly main-without-these-11, so there is no inherited drift to correct. RULE CHANGE 2026-08-23: `collected` is now an EQUALITY, not a minimum. Every floor entry above this one records the number being found BELOW reality and hand-raised after the fact -- 21 low at the worst, then 8, then 1, then 2 -- because nothing ever required a test-adding PR to touch this file, so the permissive direction was silent by construction and the rule 'measure on the merge result' had to be restated three times with nothing enforcing it. verify.py now FAILS when collected exceeds the floor, printing the two integers to write. That also makes the concurrency case self-enforcing: once every test-adding branch must edit these same two lines, two concurrent branches CONFLICT IN GIT, so the second cannot merge without rebasing onto the first and re-measuring on the actual merge result. Demonstrated repeatedly on the change itself: six merges landed on main in the two hours it took to write, moving this file 368 -> 387 -> 391 -> 402, and every one would have left the floor below reality under the old one-directional rule. `passed` deliberately stays a MINIMUM on passed+skipped: only collection is machine-invariant (a skipped test is still collected), measured across machines at 391 collected on both, with pass/skip splits of 365/26 on CI against 391/0 locally. The *_max ceilings are untouched by this change and nothing new is skipped. `--update-floor` also stops REPLACING this note -- it appends -- so the warning above about restoring it by hand no longer applies; and drift does NOT block --update-floor, since a gate that forbade its own only remedy would be a deadlock (the first draft was exactly that). FLOOR 402 -> 407 on 2026-08-23 (findability admission requirement). (findability admission requirement). (findability admission requirement). (findability admission requirement). Exactly five new pytest tests, all in test_capability_admission.py: test_findability_distinguishes_its_three_sub_causes, test_findability_blocks_new_capabilities_and_reports_older_ones_as_debt, test_unreadable_reach_is_not_evaluated_and_never_a_failure, test_findability_exemption_is_declared_in_code_not_in_a_live_ledger, test_consult_sites_are_falsifiable_claims_about_real_callers. NO CEILING MOVED and nothing new skips: all five build synthetic ledgers in a tempdir or read committed tables, so none needs a populated capability ledger, an agent CLI or ~/.claude/skills. The one machine-dependent thing they touch -- an external consult site declared in capability_advisor.CONSULT_SITES whose skill prompt is not on this machine -- is reported as UNVERIFIED rather than skipped, because absence of the caller is not refutation of the claim; the in-tree site (tick) is asserted verified on every machine so the check can never degrade into 'everything unverified, nothing tested'. Measured on the merge result per the rule above: this file CONFLICTED three times while the branch was open, as main went 387 -> 391 -> 402 (#61, #64, #65, #60). Each time it was resolved as the UNION rather than by taking a side, and the count was RE-MEASURED on the new merge result rather than either number being carried forward: 402 (main at bd6da2e) + 5 (this branch's new tests) = 407. That is the rule this file already states -- measure the floor on the merge result, not on the branch -- and it mattered here, because #60 both deleted test_ci_gate_config.py and added more than it removed, so guessing in either direction would have been wrong. -> re-measured on 2026-08-23 (PR #62, the four deferred #42 review findings): three new tests, all machine-independent (each builds its own tmp_path Brain and manifests), so NO CEILING MOVED and nothing new is skipped. Fourth conflict for this branch, and the first one under the EQUALITY -- which is the point: the equality's own rationale says git conflict detection is what enforces 'measure on the merge result', and that is exactly what happened here. Under the old minimum the three earlier conflicts could each have been resolved by keeping the larger number; under the equality the count MUST be measured, and it was. RESOLVED AGAINST #68 (findability admission requirement) on 2026-08-23: taken as the UNION per the rule this file states -- #68's five-test entry is retained above and this branch's three-test entry beside it -- and the count RE-MEASURED on the merge result rather than keeping either side's number. main fc1fd42 collects 407; this branch adds 3; 410 measured with `pytest --collect-only -q` on the merge result, not assumed. Ceilings untouched at 26/7/2 and nothing new is skipped. Also resolved in the same merge: langsmith-fleet-worker-attempt.json, a CI-emitted `langsmith-fleet/v1` worker-attempt record whose two sides differed only in `emitted_at` and `pr_number` (62 here, 68 on main). Main's NEWER record was kept rather than this branch's older one -- discarding a newer provenance observation to win a merge would corrupt exactly the causal-provenance evidence CLAUDE.md 2 protects, and this branch's own run re-emits its record anyway. FLOOR 410 -> 411 on 2026-08-23 (CodeRabbit follow-up on PR #42, thread 3837879039; re-measured again after #56 made `collected` an EQUALITY, which makes an assumed number a hard RED rather than a quiet pass -- main stayed at 402 across #56, and the merge result measures 403, so #56 added no collected tests and this is main's 402 plus this branch's one): exactly one new test, test_feedback_model_provenance.test_late_sweep_completes_terminal_attempts_never_one_in_flight, which pins that ledger_reconcile.resolve_unresolved_worker_attempts completes only TERMINAL unresolved worker attempts and never one still in flight. No ceiling moved and nothing new is skipped -- the test builds its own tmp ledger and codex rollout fixture and monkeypatches adapters.CODEX_SESSIONS, so it needs no agent CLI and no populated capability ledger and runs on a bare runner. RESOLVED AGAINST #59 (improvement-log accessor), which raised the floor 387 -> 391 on main while this branch raised it to 388: taken as the UNION -- #59's rationale is retained above and the count was RE-MEASURED on the new merge result rather than keeping either number, which is the rule this file states and the mistake that once put the floor 8 below reality. 391 (main, incl. #59's four tests) + 1 (this branch's one new test) = 392 measured, not assumed: 392 passed, 0 failed, 0 skipped, 83/83 selftests, 43/43 can-fire, 5/5 gates. Three sibling follow-up branches are in flight against this same main (CI/ruff config, arm-attribution + durability, adapters label->ID); if this file conflicts with one of them, resolve as the UNION and RE-MEASURE on the new merge result rather than taking either number -- that is what #42 and #50 did, and taking a side is what put the floor 8 below reality earlier. RESOLVED AGAINST #68 (findability admission requirement) on 2026-08-23: taken as the UNION per the rule this file states -- #68's five-test entry is retained above and this branch's one-test entry beside it -- and the count RE-MEASURED on the merge result. main fc1fd42 collects 407; this branch adds 1; 408 measured with `pytest --collect-only -q` on the merge result, not assumed. Ceilings untouched at 26/7/2 and nothing new is skipped -- the one new test builds its own tmp ledger and codex rollout fixture, so it runs on a bare runner. Also resolved in the same merge: langsmith-fleet-worker-attempt.json, a CI-emitted `langsmith-fleet/v1` worker-attempt record differing only in `emitted_at` and `pr_number`; main's NEWER record was kept, since discarding a newer provenance observation to win a merge would corrupt the causal-provenance evidence CLAUDE.md 2 protects. FLOOR 411 -> 415 on 2026-08-23 (PR #70 diagnostics salvage): four new collected tests in test_capability_set_coverage.py from the PR #43 salvage plus CodeRabbit follow-ups on PR #51/#70 \u2014 union/missing-candidate fetch command, truncation after six modules, AST-scoped gate-call audit, and entrypoint-diagnosis coverage. NO CEILING MOVED and nothing new is skipped; all inject synthetic ledgers or read committed source. Measured on the merge result at 91d37fa: 389 passed + 26 skipped = 415 collected on CI, not assumed. FLOOR 415 -> 416 on 2026-08-23 (the dangling-citation follow-up, PR #74): exactly ONE new test, test_ci_gate_config.test_every_cited_repo_path_resolves, which reads the two committed config files this repo OWNS (the pin file and ruff.toml) and asserts every repo-relative path they cite exists. It exists because the pin file shipped citing docs/ci/LINT_BASELINE.md when the real path was docs/CI_LINT_BASELINE.md: the sibling checks read that file's CONTENTS thoroughly and its PROSE not at all, and the prose is the only pointer telling a reader where to re-measure before bumping a pin. Scoped to the two owned files deliberately -- scanning pr-00-gate.yml yields six findings that are all correct as written (guarded by hashFiles or a .agents check, or upstream paths), and a test that cries wolf gets waived. NO ceiling moved. RE-MEASURED SIX TIMES as the base moved under this ONE-LINE change: bd6da2e 402 -> ddb0928 402 -> fc1fd42 407 -> 0d661e3 407 -> 0593eeb 411 -> 6fed4ad 415, each +1 with this test, and the branch was rebuilt on each rather than the number carried forward. THIS BRANCH IS THE WORKED EXAMPLE of the equality's concurrency cost, so record it rather than rediscover it: main moved EIGHT times in the ~2.5 hours a one-line comment fix was open (#56, #68, #73, #69, #62, #70 and two direct commits), the floor line conflicted THREE separate times, and two merges overlapped the change directly -- #73 landed a byte-identical copy of the backplane-conformance.yml guard this branch also carried (dropped as redundant), and #69 edited this very test file in a neighbouring region. The equality is still the right call and should stay: every entry above this one records the floor being found BELOW reality, which is the permissive direction. But no amount of author care wins this race, because the correct value is only knowable on the merge result. The durable fix is CI running `verify.py --update-floor` on the merge commit, which keeps the equality and removes the race; until then a test-adding PR must be merged promptly after going green, because it re-conflicts on roughly every subsequent merge. FLOOR 416 -> 427 on 2026-08-23 (PR #72 hygiene untrack, rebased after #71 merged): exactly 11 new tests from test_repo_artifact_hygiene.py with root-anchored gitignore patterns. NO ceiling moved. Measured on merge result after #71 landed on main: 416 (main) + 11 = 427 collected via pytest --collect-only -q, not assumed. #71's simpler untrack landed first; this branch carries the full hygiene test suite and corrected root-anchored patterns. FLOOR 427 -> 428 on 2026-08-23 (PR salvaging #34/#42 remnants): exactly one new test, test_feedback_model_provenance.test_gemini_provenance_reads_the_per_run_log_before_the_conversation_store, recovered from #42's post-merge commit 4e0d6ae along with the adapters catalog work it exercises. No ceiling moved and nothing new is skipped -- it seeds adapters._ADVERTISED_MEMO instead of letting the catalog probe shell out, so it runs on any machine and adds no prerequisite. `passed` is 428 rather than the 426 verify.py suggested on this machine: two test_capabilities liveness tests (test_gate_blocks_execution_is_opt_in_and_narrow, test_evidence_gate_kind_is_not_blanket_observer) currently fail HERE on pristine main as well, because the hourly fleet tick mutated the machine-local ledger and range-lane-rollout now classifies matched_not_invoked rather than deliberately_gated. That is ledger STATE, not this branch and not the code -- CI bootstraps an empty ledger and counts 428/428. Recording 426 would have baked a local environment failure into the floor as though it were the expected result. FLOOR 428 -> 441 on 2026-08-23 (coverage measures what actually runs): exactly 12 new tests, all in test_verify_coverage_mode.py. They read committed files and verify.py's own source, and monkeypatch verify.COVERAGE in-process -- no populated ledger, no agent CLI, no ~/.codex, and no coverage RUN -- so all 12 execute on any machine and NO ceiling moved. The change itself is a measurement fix, not a gate: `verify.py --coverage` wraps each child in `coverage run --parallel-mode` and combines, because the per-module --selftest is a SUBPROCESS and a pytest-only coverage run cannot see it. That blind spot was most of the codebase -- 78 modules have no test_*.py at all, ~85,500 lines, 79.6% of non-test root Python -- so the reported 48.45% was measuring the gap in the instrument, not a gap in the tests. Combined: 76.1% (45,049 statements, 10,774 missed). Twelve of the twelve modules the old report named as worst were selftest-only; outcomes.py reported 9.0% and measures 61.9%, watch.py 9.6% -> 86.9%. Coverage is OFF by default and deliberately never touches the exit code -- one of the 12 tests pins that, because enforcing a threshold here would reward pytest wrappers around already-tested modules: metric up, assurance flat. Measured on the MERGE RESULT: branched from origin/main bcc68cd (floor 427), then REBASED onto f5f1c39 when it landed underneath and re-measured on the new merge result rather than carrying the old number: 428 + 13 = 441. +1 on 2026-08-23 (440 -> 441 after the rebase), same branch: test_the_cli_help_actually_renders. It exists because this branch BROKE `verify.py --help` and its own twelve tests did not notice. argparse interpolates help strings with `% params`, so the literal `~80%` in the --coverage help was read as an `%o` octal conversion and --help died with 'badly formed help string'. All twelve original tests passed: every one inspected source text or monkeypatched a flag, and not one RENDERED the help -- a construction-time test suite that never exercised the constructed thing, which is this repo's founding defect one layer up. CI's verify.py gate caught it, which is the check of last resort working as intended. The new test runs `verify.py --help` as a subprocess and asserts it exits 0, so it runs anywhere and NO ceiling moved. Rendering rather than grepping for `%` is deliberate: a grep would flag the legitimate `%(default)s`. FLOOR 441 -> 442 on 2026-08-23 (matched_not_invoked yields to observers and declared gates): exactly one new test, test_capabilities.test_matched_not_invoked_yields_to_observers_and_declared_gates. It is SYNTHETIC on purpose and that is the point of it: the two tests that caught this bug in the wild read the LIVE ledger, so they skipped with a named reason on the empty ledger ci.yml bootstraps -- the defect was red on every populated machine and green on CI for as long as it existed. A synthetic row asks the same question everywhere, so this one RUNS on a bare runner and NO ceiling moved. The fix itself moves matched_not_invoked below `observing` and below the DECLARED deliberately_gated check in classify_liveness: it was the first check, which made it the fourth instance of the unescapable label the comments in that function exist to fix. Audited before committing -- 12 of 43 live rows reclassify (ten observers to observing, two declared gates to deliberately_gated) and ZERO move for any other reason, so nothing is reclassified by inference and the weaker gate_reason-only branch is untouched. Measured on origin/main 0d9c3a7, whose recorded floor is 441, so 441 + 1 = 442. FLOOR RECORDED by verify.py --update-floor on 2026-08-23: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. FLOOR RECORDED by verify.py --update-floor on 2026-08-24: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. FLOOR RECORDED by verify.py --update-floor on 2026-08-24: collected=442, passed=442. Ceilings preserved, never re-measured \u2014 they are edited by hand. MYPY_EXEMPT_MAX introduced at 64 on 2026-08-23, the same change that flipped `typecheck` ON in pr-00-gate.yml. It was OFF because 608 whole-tree errors were drainable 0 per PR -- a gate whose clear path is blocked by the thing it measures. Two real changes opened it: the src/ move scoped the Gate's `target=src` to the 99 modules (608 -> 467), and pyproject.toml's [[tool.mypy.overrides]] exempts the modules that still have findings BY NAME so the 35 already-clean ones are checked today. 12 var-annotated findings were then drained (467 -> 455, 66 -> 64 modules) to prove the drain works rather than promise it. This ceiling may only ever be LOWERED, by typing a module and deleting its line -- raising it means agreeing one more module goes unchecked, so say which and why. NOT a suppression: no error code is disabled anywhere and the 455 stay visible via `python3 scripts/ci_lint_baseline.py`. Edited BY HAND like the other *_max values; --update-floor never re-measures it. MYPY_EXEMPT_MAX LOWERED 64 -> 43 on 2026-08-24, the ratchet's first real drain: 21 modules typed clean and removed from the exempt list, 455 -> 430 findings. Targeted the modules with 1-3 errors on purpose -- fixing 50 errors spread across the big modules would move this number by ZERO, and this number is what the gate reads. Lowering it is the drain; it may never be raised without naming the module and why. MYPY_EXEMPT_MAX LOWERED 43 -> 33 on 2026-08-24 (batch 2 of the drain): the whole <=5-finding tail typed clean. 66 of 99 modules now checked. What remains is five per-module campaigns (capability_advisor, dispatcher, capability_propensity, runtime_ac_gate, capabilities hold most of it), so future batches take ONE big module at a time rather than skimming. (batch 2 detail: 43 -> 26, 430 -> 374 findings, 73 of 99 modules checked. `mypy_path` gained `tests` so mypy can RESOLVE the recurrence-fixture roster capability_admission legitimately imports; the target stays `src`, but mypy then follows into that one test file, whose two findings were fixed rather than configured around.) MYPY_EXEMPT_MAX LOWERED 26 -> 20 on 2026-08-24 (batch 3): 79 of 99 modules checked. FLOOR 442 -> 448 on 2026-08-24 (absent-check detector + its ratchet): SIX new tests in tests/test_checks_reported.py holding the frequency rule and the expected-check ratchet. NO ceiling moved -- they call pure functions and read one committed JSON file, so they run on any machine. The ratchet exists because dogfooding the detector caught it DISARMING ITSELF: while pr-00-gate.yml sat held, every merged PR merged without the Gate, so after twelve such merges the Gate's checks fell below the 75% frequency threshold, the expected set eroded 23 -> 14, and PR #91 was pronounced clean by the tool written to catch exactly that. config/expected-checks.json is the high-water mark, seeded from PRs #87/#89 whose Gate demonstrably ran, and it only comes down when somebody deletes a line. LOWERED to 16 on 2026-08-24 (batch 4, re-applied after merging main, which carried #94/#100's own floor work \u2014 main's note kept, only the bound re-set). LOWERED 16 -> 16 on 2026-08-24 (batch 5): 83 of 99 checked. LOWERED 16 -> 12 on 2026-08-24 (batch 5): 87 of 99 checked. Lesson recorded three times now: measure with the PROJECT run, never per-file \u2014 `mypy src/X.py` reports clean for modules the project run still flags. FLOOR 448 -> 450 on 2026-08-24 (giving the per-node deliberate-break finding its consumers): exactly two new pytest tests, both in test_synthesis_promotion.py -- test_a_passing_break_names_the_tautologies_it_carried and test_the_break_caveat_is_silent_on_clean_and_on_pre_per_node_evidence. NO CEILING MOVED and nothing new is skipped: the first builds its own git repo in tmp_path and runs the real verification path (git + pytest only, no populated ledger, no agent CLI, no ~/.codex), and the second is a pure unit on _break_caveat. The third piece of this change is a runtime_ac selftest case, which adds NO collected test -- runtime_ac has a --selftest and synthesis_promotion does not, which is the whole reason only one of the two needed pytest tests. Measured on the merge result: branched from origin/main 8549f84, whose recorded floor is 448, and re-checked that main had not moved before writing this. 448 + 2 = 450, from verify.py's own count (450 passed, 0 failed, 0 skipped, 85/85 selftests, 5/5 gates), not assumed. FLOOR 448 -> 451 on 2026-08-24 (offload guidance correction, PR #113): THREE new tests in test_repo_artifact_hygiene.py -- two parametrized entries adding src/UNKNOWN.egg-info/PKG-INFO and UNKNOWN.egg-info/PKG-INFO to EMITTED_ARTIFACTS, and test_no_build_metadata_is_tracked. NO ceiling moved and nothing new skips: all three shell out to git against this checkout, so they run anywhere git does. They exist because THIS PR carried four committed build artifacts and no check objected. A CI step runs `pip install -e .`, setuptools writes src/UNKNOWN.egg-info/ (named UNKNOWN because pyproject.toml declares no [project] on purpose), and the repo's own autofix bot committed all four -- .gitignore had no *.egg-info/ entry and this suite only knew the langsmith-fleet family. CodeRabbit caught it; nothing local did. Both locations are listed because a checkout builds into src/ while the EXEC MIRROR IS FLAT. The tracked test is separate from the ignored test on purpose: the four files were committed BEFORE the pattern existed, and adding a pattern does nothing to a path git already tracks -- so an ignore-only check passes on a repo still carrying the debris. RESOLVED AGAINST #110 and #114 on 2026-08-24, which moved main 448 -> 450 while this branch raised it to 451: taken as the UNION per the rule this file states -- main's entries are retained above and this branch's three-test entry beside them -- and the count RE-MEASURED on the merge result rather than either number being carried forward. 450 (main) + 3 (this branch) = 453, measured with `pytest --collect-only -q`, not assumed. Ceilings untouched and nothing new is skipped. FLOOR 453 -> 458 on 2026-08-24 (untracking .coverage, the coverage database this repo's own verify.py writes): exactly FIVE new tests, all in test_repo_artifact_hygiene.py -- two parametrized ignore cases over COVERAGE_DATA_FILES (.coverage and a parallel-mode .coverage...), one untracked case, and two parametrized must-stay-committable cases (.coveragerc, tools/coverage_guard.py). NO CEILING MOVED and nothing new is skipped: all five ask GIT about a path in this checkout (check-ignore / ls-files) and need no populated ledger, no agent CLI and no ~/.codex, so all five RUN on a bare runner. The change is `git rm --cached .coverage` plus root-anchored `/.coverage` and `/.coverage.*`; the 90 KB SQLite database arrived on main in #109, a typing PR whose every other file is about mypy, and verify.py's coverage_reset() unlinks it and coverage_combine_and_report() rewrites it on every --coverage run -- so while tracked it was an opaque binary rewritten by the command that produces this repo's verdict. Both patterns because two different steps write them, and `/.coverage.*` rather than `/.coverage*` because the second swallows .coveragerc. Break -> revert performed in all three directions and recorded in the test file. Measured on the MERGE RESULT: fast-forwarded onto origin/main 5c769e0, whose recorded floor is 453, and re-fetched to confirm main had not moved again before measuring. 453 + 5 = 458 from verify.py's own count (458 passed, 0 failed, 0 skipped, 85/85 selftests, 5/5 gates), not assumed. LOWERED 12 -> 10 on 2026-08-24 (batch 6): keepalive_outcomes (17 findings) and redirect_sweep (18) typed clean, 89 of 99 modules checked, 240 -> 205 findings. Both modules were drained by WRITE-MODE isolated offloads (dispatcher.offload --isolate) rather than in-seat, and every diff was reviewed here before it was applied -- the two changes that could have altered behaviour were checked first-person and both are equivalent: redirect_sweep's marker-rc read keeps the same except clause wrapping it, so a non-numeric rc still raises inside the try and still lands at None; keepalive_outcomes' added `oc is not None` guard short-circuits ahead of _should_record_outcome, whose own first line already returns False on oc is None. LOWERED 10 -> 7 on 2026-08-24 (batch 6b): dispatcher (65 findings), capability_propensity (47) and runtime_ac_gate (34) typed clean, 92 of 99 modules checked, 205 -> 91 findings. All three drained by write-mode isolated offloads and then INTEGRATED BY HAND rather than applied as returned -- three of the agents' choices were replaced with smaller ones, and the reasons are the durable part. (1) capability_propensity came back with two `type: ignore`s at the correlated-arm lookup, justified as an ambiguous key type. The ambiguity was real but the fix was in the wrong file: research_subjects.reciprocal_evidence_weights is GENERIC over its member type -- run-id strings at one caller, verdict indices at the other -- and its `dict[str, float]` return was too narrow. It is a TypeVar now and both ignores are gone. Suppressing there would have hidden a false positive AND blinded the one check that would catch a real key-type mismatch in the correlated-arm discount, which CLAUDE.md 2 makes load-bearing. (2) capability_propensity.detect() came back rewritten into four locals; behaviour-preserving (verified: same objects mutated, same key insertion order) but one `out: dict[str, Any]` annotation does the same job, so the annotation was taken instead. (3) runtime_ac_gate's spec_path came back with `spec_dir or env.get(K, DEFAULT)` split into branches, which silently falls back to DEFAULT where the original raised TypeError on Path(None). Unreachable via os.environ, but this is a GATE and 'silently use a default' is the permissive direction; with env typed as Mapping[str, Any] the original one-liner type-checks unchanged, so the restructure bought nothing. Asserts added in production paths were each checked against their invariant rather than trusted: dispatcher's `profile_attempt_id is not None` sits inside `elif selected_profile:` where line 815 makes it non-None by construction, and attach_profile_attempt_to_decision already declares `decision_id: str`. LOWERED 7 -> 1 on 2026-08-24 (batch 6c): capabilities (14 findings, ONE missing annotation on KNOWN_GATES caused all of them), router (16), and the last five findings across feedback/runtime_ac/capability_outcome_bridge/capability_compiler. 98 of 99 modules now checked; only capability_advisor (24) remains, held back because PR #113 edits the same file. The drain added ZERO net type: ignore -- router's one remaining suppression predates this work. THE DRAIN FOUND A REAL BUG and the value is in how it was handled: router's load_backlog promised a bare-list payload in its own docstring and could never read one, because `.get` on a list raises AttributeError into a broad `except` that returns [] -- so a populated backlog read as NO WORK. The offloaded agent was told to report suspected real bugs rather than fix them, did exactly that, and left a type: ignore documenting it; the fix landed here instead, with a selftest asserting a non-empty list survives (an empty one cannot distinguish the fix from the failure, since [] is also what a parse error returns) and a break->revert showing the old one-liner returning [] for a valid backlog. Latent, not live: the file on disk is the {\"items\": [...]} dict form, whose behaviour including its no-items fallback is unchanged. Worth closing anyway -- the day discovery writes the documented shape the symptom would be silence, which is this repo's signature failure. LOWERED 1 -> 0 on 2026-08-24 (batch 6d, THE LAST ONE): capability_advisor's 24 findings typed, the [[tool.mypy.overrides]] block removed entirely, and all 99 of 99 modules now checked. 240 findings drained to zero across the campaign with ZERO net `# type: ignore` added. THE LAST MODULE EXPOSED A LATCHED GATE IN THE RATCHET ITSELF, which is the durable finding here. verify.mypy_exempt_modules() returned None for BOTH 'pyproject unreadable' and 'readable, no override block' -- so the run that FINISHED the drain would have printed 'mypy ratchet: NOT COUNTED', the gate going silent at the exact moment it succeeded. Its own docstring already argued against this ('a ratchet that stops being counted is indistinguishable from one that emptied, and only one of those is good news'), and two more things prove it was unintended: _format_mypy_exempt_line carried a ' -- fully drained' branch no input could reach, and the selftest asserted the function was TRUTHY, so an empty list failed it. That is the CLAUDE.md pattern exactly -- a gate whose clear path is blocked by the thing it measures -- and it would have latched on the last module. Fixed: [] now means answered-and-empty, None stays unanswerable, and the selftest asserts all three renderings plus `is not None` rather than truthiness. `collected` is unchanged at 453: the new coverage lives in verify.py's own selftest, which pytest does not collect. FLOOR 500 -> 502 on 2026-08-25 by the capacity-arity branch: +2 tests, both pinning that `capacity.compute` returns THREE values on every branch. It used to return 2 or 3 depending on which of `_classify`'s 21 returns ran, and the first of those is the 429-shed check, whose `_shed` reads a file on the HOST outside `$ORCH_STATE_DIR` \u2014 so the ARITY was a property of the machine, and `test_capacity_gate_is_seat_level_not_gemini_special` died on the unpack locally while CI, where nothing is shed, stayed green. Measured on the merge result AFTER rebasing onto origin/main, which had taken #140 (500) mid-session: 502 on the rebase, not 460 on the pre-#140 branch, which is exactly the drift this equality exists to catch. MIRROR CEILING ADDED 2026-08-29: `mirror_skipped_max` = 31. `skipped_max` was ONE number bounding TWO different deprived environments, and this note above says which one it was measured in -- a GitHub runner with none of this instance's local prerequisites. The EXEC MIRROR is a different shape: every local prerequisite is present there (it exists only on the machine the system runs on, so nothing skips for a missing CLI, skill, app bundle or ledger row) but it is a FLAT FILE COPY, so it is not a git repository and has no .github/. Measured on unmodified main: 471 passed, 31 skipped -- 12 from `repo_files_absent('.github/workflows', ...)` in test_ci_gate_config.py and 19 from `git_repo_absent()` in test_repo_artifact_hygiene.py, and ZERO runner-shape skips. Against the 26 measured on a runner that skips neither family, `python3 verify.py` from ~/.codex/orchestrator-mirror was RED ON EVERY INPUT, including an entirely correct tree -- confirmed structural by syncing the PREVIOUS main into a scratch mirror and getting the same 31. CLAUDE.md 1 makes the mirror run the verdict ('cmp-clean is not agreement'), so the one instrument that catches cross-tree divergence had stopped protecting anything, and a gate red whatever you do is a gate that gets switched off. NOT FIXED BY RAISING `skipped_max` TO 31: CI runs on a runner where 26 is the correct bound, and the extra five would have let a runner skip five more checks in silence -- one gate going quiet to un-stick another. Instead each shape carries its own agreed number, selected by `env_prereq.exec_mirror_shape()`, which detects the PREREQUISITE (no .github/ AND git reports no repository) and never `$CI`, exactly as every other detector in that module does. Both marks are required because the mirror number is the LOOSER one, so an ambiguous tree falls back to the base. The mirror value REPLACES the base rather than adding to it: the runner-shape absences the base pays for cannot occur on the mirror, and 26+31 would have bought 26 units of headroom nothing there can legitimately spend. WHAT DRAINS IT: making a mirror-skipped test runnable there. Teaching orch-sync-mirror.sh to copy .github/ would drop 12 and this number must come down to 19 IN THE SAME CHANGE -- but note that script lives outside the repo, so a mirror synced by an unpatched copy would then be red at 31 > 19, which is the disease this entry cures; do it only if the script's state can be relied on. The 19 git-shape skips are irreducible while the mirror is a file copy. Any ceiling key may carry a `mirror_` variant by the same derived rule; only this one is set, and an unset variant falls back to the base agreement, which is the strict direction. FLOOR 502 -> 515 on 2026-08-26 (escaped-defect test priority): exactly THIRTEEN new tests, all in tests/test_escaped_defect_priority.py. NO CEILING MOVED and nothing new is skipped: each builds its own git repository in tmp_path or calls a pure function, so none needs a populated ledger, an agent CLI or ~/.codex, and all thirteen run on a bare runner. They are pytest rather than selftest cases ON PURPOSE, and the reason is the subject of the module: local_verify grades per pytest NODE, so a selftest is one node and its internal assertions are invisible to hollow-test detection -- a module that orders test-writing work should have its own tests gradeable by the gate that judges the work it orders. The module ALSO keeps a --selftest (88 of 88 now), which exercises the CLI the way it ships including live git log parsing; the two are complementary, not alternatives. Two of the thirteen caught a real defect in the module during review: the first implementation multiplied the tiers apart (1e6/1e3/1) and summed them, which holds only while the lower tiers stay small -- at 1,000,000 uncovered statements tier 3 exactly equals one escaped defect and the ordering the module exists to guarantee silently inverts. Replaced with a lexicographic tuple, which holds at any magnitude, and the tests now assert at 10**9 rather than a modest number. Measured on the MERGE RESULT: branched from origin/main b80e6e4, whose recorded floor is 502, and re-fetched to confirm main had not moved before measuring. 502 + 13 = 515 from verify.py's own count (515 passed, 0 failed, 0 skipped, 88/88 selftests, 5/5 gates), not assumed. RESOLVED AGAINST #152 on 2026-08-26, which landed underneath this branch and added a NEW floor key, mirror_skipped_max. Taken as the UNION per the rule this file states: main's structure kept in full including that new key, this branch's rationale appended rather than either note replacing the other, and the count RE-MEASURED on the merge result rather than carried forward from either side \u2014 515 from verify.py's own run after the rebase, which confirms #152 added no collected tests. Taking a side here would have been silently correct this time and is exactly what put the floor 8 below reality on an earlier occasion." }