Repository navigation
Conversation
leaderboard_impact.py copied the working tree's run files and compared against the working tree's payload, so regenerating after release dashboard-data-20261006 (#202) scored that release's payload and exclusions and silently rewrote 19 of the 23 committed verification/leaderboard_impact* files (1,928 -> 1,920 scored cells). The script now stages the run bundle from 8b4c0ca (release dashboard-data-20260930) and proposed_changes.json from 4db91b5 (#200) with git show, checks each file against its pinned sha256 and stops before scoring on any mismatch. It reads only the staged copies. --out-dir writes the evidence elsewhere. tests/test_reference_adversary_impact.py checks the pins, the refusals (including #202's payload), that a checkout whose working-tree run and proposals are junk still scores the pinned bytes, and (slow) that a full regeneration reproduces all 23 committed files byte for byte. The README lists every pinned input with its sha256 and the scripts that read it, and the Reproduce steps restore the whole run and annotations directory from 8b4c0ca for the scripts that still read the working tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Hypothesis test flips, truncates or extends any pinned run file and requires git_input to refuse it without writing the target, and to accept the unchanged bytes. A differential check requires the payload pin to equal definition_conformance.py's and publication_sources.py's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
definition_conformance.py, publication_sources.py, engine_probe.py and build_proposals.py read the working tree's run, which release dashboard-data-20261006 (#202) rewrote (payload and exclusion record), and the two engine-side scripts built their reference system from the working tree's convention modules, one of which #204 rewrote. Each now stages every input through scripts/pass_inputs.py, which writes `git show <commit>:<path>` to scratch and stops unless the bytes match the pinned sha256, before the script computes or writes anything. leaderboard_impact.py (#205) uses the same module, so the pins live in one place. build_proposals.py now writes to a required --out and refuses the committed proposed_changes.json, which is pinned at 3a6e5920. No evidence file, reference, exclusion or score changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inked --out _identical_but now requires the regenerated file to carry the same key, indentation and punctuation on each volatile line, so only that one JSON value may differ; a new test feeds it a changed value elsewhere, a renamed key, re-indentation, a changed list and a dropped line. The build_proposals refusal test adds a relative path and a symlink to the committed proposed_changes.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Merge-order note, from a local merge reported by the session stacking on both PRs:
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stage before the engine Review of b4f2aac (GPT-6.1 Sol, hard tier) asked for these changes. - policybench loads benchmark_specs.json itself, as policybench.scenarios is imported, so the engine-side scripts computed with the checkout's definitions while recording the pinned copy's hash. Each process of definition_conformance.py, publication_sources.py and engine_probe.py, workers included, now stages the pinned definitions and points policybench at them (pass_inputs.use_staged_specs) before importing anything else from the package. The test fence no longer exempts the package's own read for these scripts. - publication_sources.py loses --from-facts, which built both reports from a saved file with no pin check. - The conventions and definitions are staged before the engine is imported, hooked or handed to a worker, not after. - Workers are started with the spawn method and an initializer that stages, so a forked worker cannot inherit the main process's system. - build_proposals.py creates --out exclusively, so a hard link to the committed proposed_changes.json or another checkout's copy is refused like any existing file. - leaderboard_impact.py keeps the caller's PYTHONPATH for its scoring child, and its slow regeneration now runs fenced in both processes. - Each script checks that the pass_inputs it imported is the one beside it. No evidence file, reference, exclusion or score changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o the passes' inputs (#206) * Pin the October 5 audits' leaderboard impact to the passes' inputs The leaderboard_impact.py scripts in reference_audit/2026-10-05, 2026-10-05-louisiana, 2026-10-05-medicare-part-b and 2026-10-05-payroll read the frozen run from the working tree. Release dashboard-data-20261006 (#202) rewrote it, so on main three of them stop on duplicate exclusions and the Louisiana one exits 0 after rewriting both of its evidence files. Each script now stages every input it reads with git show at the pass's commit (the run at 8b4c0ca, the audit's inputs at 9ce4ade), checks a pinned sha256, and reads only the staged copy. --out-dir writes the evidence elsewhere, and --scratch must lie outside the repository. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pin the SALT and Part B proposals and the payroll answers to the passes' inputs reference_audit/2026-10-05/scripts/propose_exclusions.py, 2026-10-05-medicare-part-b/scripts/propose_exclusions.py and 2026-10-05-payroll/scripts/model_answers.py also read the frozen run from the working tree. On the run #202 left, the SALT proposals exit 0 with no proposals, the Part B proposals stop on already-excluded outputs, and the payroll answers flip the scored column on 184 rows. Each now reads every input with git show at the pass's commit, checks every pin before reading or writing anything, and takes --out-dir. The tests (renamed tests/test_reference_audit_pins.py) rerun all three from a checkout whose working tree is junk and require the committed files byte for byte. Each audit's README lists every pinned input with its sha256. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refuse every pinned input before scoring or writing, input by input Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read the adversary's run pins from pass_inputs.py when #207 has landed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Answer the review: refuse a scratch holding the checkout, test from a junk checkout - The four impact scripts refuse a --scratch that contains the repository, not only one inside it: pass_inputs() and stage() delete scratch/<name>, so --scratch /tmp with the checkout at /tmp/pass_inputs/... deleted it. - The junk-checkout tests now load each script from the junk checkout, so HERE and ROOT both point there; a read through HERE no longer escapes. - They run always_zero and Part B's legacy impact summary for real on the staged copies, and require the committed always-zero baselines and legacy summaries, so reads inside those helpers are covered too. - Tests use a self-cleaning scratch directory instead of tmp_path, which pytest keeps for three runs (about 2 GB here). - Louisiana's README: the exclude option scored 1,918 instead of 1,926. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Answer round 2: compare scratch by filesystem identity, run junk tests in the junk - The impact scripts' overlap guard compares device and inode instead of path spelling, so a parent spelled in another case on a case-insensitive filesystem (or reached through a link) is refused too. - The junk-checkout tests chdir into the junk checkout and pass its copy of the script as argv[0], so a read relative to the working directory no longer reaches the real checkout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Enter the junk checkout before loading the script, so import-time reads land in the junk Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Brings the branch up to date with main at 875fe1f (release 20261027, #247). No conflicts. #206 (on main) made test_the_pins_agree_across_the_scripts_and_with_the_adversary read this branch's scripts/pass_inputs.py when it exists, and its PASS_COMMIT, RUN_PATH, RUN_SHA256 keys and payload pin equal the test's, so the merge order problem noted on this PR on 2026-10-09 no longer arises. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Ready for hub review This is an independent review of #207 by a Claude Opus 5.5 cloud session, written for the PolicyBench owner session. Verdict: REQUEST_CHANGES at The PR's head on GitHub is still
Blocking1. The README's reproduce environment is wrong at the head, and following it writes different evidence with exit 0
That was true on the branch before the merge. It is false at the head. #247 ( I followed the README's own probe command at the head in that environment: The script exits 0 and writes a probe whose scored reference no longer reproduces. This PR exists to stop that kind of silent divergence. The inputs are pinned, but the documented environment now hands the scripts a different engine. The slow tests' instruction at the end of the README ( The fix needs two parts. (a) README text. Replace the sentence at line 540 with:
I checked this route. (b) A version check. Have ENGINE_VERSION = "2.15.17"
def require_engine() -> None:
from importlib.metadata import version
found = version("policyengine-us")
if found != ENGINE_VERSION:
raise SystemExit(f"the pass's reference system is policyengine-us {ENGINE_VERSION}, not {found}")Call it first in each engine-side Should fix2. The hard-link tests link the repository's committed
|
# Conflicts: # reference_audit/2026-10-05-reference-adversary/README.md # reference_audit/2026-10-05-reference-adversary/scripts/leaderboard_impact.py # tests/test_reference_adversary_impact.py
…ment Main's uv.lock installs policyengine-us 2.60.0 since #247, so the README's `uv sync --locked` no longer gave the scripts the pass's engine, and engine_probe.py wrote a probe whose scored reference no longer reproduced, with exit 0. pass_inputs.require_engine() now stops definition_conformance.py, publication_sources.py and engine_probe.py under any policyengine-us but 2.15.17, before they stage anything, and the README builds that environment from the lock at 4db91b5. A differential test ties the pin to the version the run's references and the conformance record name. The hard-link tests now link a copy of the committed records, so a regression cannot write through into the checkout. The README also notes that two slow tests fail on Linux x86_64 by a last float digit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The staged-definitions test ran definition_conformance and then publication_sources in one loop, so publication_sources read the definitions definition_conformance had staged: with its own use_staged_specs call removed, the test still passed. Each script now starts from the package's own definitions, and the test checks that policybench reads the copy that script staged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The spawn test read each pool's start method on macOS, whose default is spawn, so a pool that left the method to the platform still passed there. The test now makes fork the default, as Linux's is before Python 3.14, so dropping mp_context fails it on any system. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
#205 (
f0f7fae3on main) pinnedleaderboard_impact.pyto the reference adversary pass's inputs. This PR pins the audit's other four scripts to the same inputs, and the three that run the engine check its version first:definition_conformance.pypublication_sources.pyengine_probe.pybuild_proposals.pyOne module,
scripts/pass_inputs.py, now holds the pins and the checks, andleaderboard_impact.pyuses it too.No committed evidence file, reference, exclusion or score changes, and
proposed_changes.jsonstays at3a6e5920…. The diff touches only the audit's scripts and README, plus tests.I merged main in as
734e6b20(main atec5413c3). At that merge, the PR's diff against main equaled its diff before the merge (git diff b59570d1 e4d7e6e0), byte for byte.The bug on main
These runs used the scripts as of #205's head (
95aaa8fb), from a clean checkout of main. On main, release dashboard-data-20261006 (#202) rewrote the run's payload and exclusion record, and #204 rewrote one convention module.bug_on_main.txt, in the folder the Review section names, records them.engine_probe.pyexits 0 and silently writes different evidence. Given the arguments that the committedco_sales_tax_refund_scenario_043.jsonrecords, it writes a probe with nopayroll_taxreproduction row. Stop scoring eight tax outputs and date the response window from the last answer (release dashboard-data-20261006) #202 later excluded that cell, and the script reads the working tree's exclusion record. By the same mechanism scenario_082's probe would lose itspayroll_taxrow too, since Stop scoring eight tax outputs and date the response window from the last answer (release dashboard-data-20261006) #202 excluded that cell as well (I deduced this from the code and didn't run it).definition_conformance.pyalso exits 0 after Pin the reference adversary's leaderboard impact to the pass's inputs #205's README Reproduce step (restore the run and annotations from8b4c0ca1). It recordslatest_alt_snap_mortgage_residence.pyas5e701f8f…(Stop passing the mortgage interest inputs policyengine-us #9605 deletes #204's rewrite) instead of the pass's00cd1388…. The engine-side scripts copied the reference system's convention modules from the working tree.build_proposals.pywrites the pinnedproposed_changes.jsonin place. Today the bytes come out identical, but only because none of its eight cells falls among Stop scoring eight tax outputs and date the response window from the last answer (release dashboard-data-20261006) #202's new exclusions.definition_conformance.pyandpublication_sources.pystop at their payload pin on an unrestored tree. They read every other input from the working tree unchecked.policybenchloadsbenchmark_specs.jsonitself as Python importspolicybench.scenarios, so the engine helpers looked up each output's engine variable and aggregation in the checkout's file. Main's copy has since changed: Add a stratified UK sample and audit outputs for round 2 of the policyengine-uk audit #215 addeduk_audit, so its sha256 reads89067ba7…against the pass'sce233f8c….uv.lockinstalls policyengine-us 2.60.0. Under it,engine_probe.pyexits 0 and writes scenario_043's probe with"recomputed": 0.0and"reproduces": false, where the pass's reference system computes 19.0.The fix
scripts/pass_inputs.py(new) holds every pin, as commit, path and sha256:8b4c0ca1;policybench/benchmark_specs.jsonat8b4c0ca1;latest_final, at8b4c0ca1;049f4f09;proposed_changes.jsonandverification/definition_conformance.jsonat4db91b5f.It also names the engine the pass ran on, policyengine-us 2.15.17, as the frozen run's
reference_outputs.csv.meta.jsonrecords it.git_inputwritesgit show <commit>:<path>to scratch and stops (SystemExit) unless the bytes match the pin.stage_run,stage_fixesandstage_specsstage sets of files.use_staged_specspointspolicybenchat the staged output definitions for the current process.require_enginestops unless it finds policyengine-us 2.15.17.Each script stages everything it reads before it computes or writes anything, and reads only the staged copy. The three engine-side scripts first check the engine, and they stage before they import it or start a worker:
definition_conformance.py:policybenchat the staged definitions before importing anything else from it;available: False;publication_sources.pydoes the same for the payload, references, sidecar, exclusion record and scenarios. It loses--from-facts, which built both reports from a saved file with no pin check.engine_probe.pystages the scenarios, references and exclusion record, then the conventions and definitions throughdefinition_conformance's builder.build_proposals.py:--out, which it creates exclusively, so it never writes a file that already exists;proposed_changes.jsonhowever the caller spells the path.leaderboard_impact.py(Pin the reference adversary's leaderboard impact to the pass's inputs #205) imports the shared pins.pass_inputs()becamestage_inputs(), and its scoring child now keeps the caller'sPYTHONPATHafter the checkout. It still scores with the checkout'spolicybench, including its output definitions, and its published-reproduction check covers that.pass_inputsfile beside it.README.
4db91b5f(Add the reference adversary and record its first run (evidence only; no score changes) #200, whichpass_inputs.pypins asRECORDS_COMMIT) and runs this checkout's scripts with its Python. It keeps the restore step only for the CLI commands, which record the payload path they receive, lists the scripts' commands into scratch, and says what each regeneration test compares.definition_conformance.jsonand theleaderboard_impact*files differ from the committed ones in their last digit, so those two slow tests fail there.Tests.
tests/test_reference_adversary_inputs.py(new) holds the tests the Invariants section describes.tests/working_tree_fence.py(new) installs an audit hook that fails any open of the working tree's copy of a pinned input, includingbenchmark_specs.json. The tests generate asitecustomizethat arms it in subprocesses and spawned workers too.tests/test_reference_adversary_impact.pynow uses the shared module, and its slow regeneration runs inside the fence. Pin the reference adversary's leaderboard impact to the pass's inputs #205's byte-refusal property test moved to the new file, where it covers every pin.Verification
Byte-for-byte regeneration. Each script, at head
f6774fd1, ran against main's working tree (#202's run, #204's module) and wrote to scratch, with the fence active in every process (pytest_slow_r2.txt, 11 passed in 27 minutes on macOS arm64):verification/definition_conformance.mdverification/definition_conformance.jsonrun.seconds(wall time) andrun.script_sha256(the script's own hash; the committed file records33ce4c3e…, the script as #200 merged it)verification/publication_sources.mdverification/publication_sources.jsonmeta.secondsverification/probes/*.json(8)verification/leaderboard_impact*(23)proposed_changes.json(via--outto scratch, fast suite)3a6e5920…I also ran the scripts without this PR's changes, after restoring the run and modules from
8b4c0ca1. Their output differs from the committed files only in the samesecondslines (manual_regeneration.txt), so the pass's environment reproduces the evidence, and this PR's changes alter none of it.The round 2 review ran the slow tests at
e4d7e6e0on Linux x86_64 with a policyengine-us 2.15.17 environment.publication_sourcesand all 8 probes regenerated byte for byte, anddefinition_conformance.mdmatched;definition_conformance.jsonand the impact files differed in the last digit of a few floats. Nobody has run the slow tests at the current head yet. Free disk on the shared host sits below the level that allows heavy local runs, so the round 3 review runs them.Tests.
5b359ce6(56 passed; pytest deselects the 11 slow tests).Mutation check.
mutants_r3.pyputs back 34 faults at5b359ce6, one at a time, and requires a fast test to fail on each. Twelve read the working tree, skip a check or stage out of order; 17 reintroduce the round 1 review's seven findings; and 5 skip or loosen the engine check. All 34 fail a test (mutants_r3_final.txt). An earlier run found three survivors, anda248bc67and5b359ce6changed the tests to catch them. One waspublication_sources.pyskipping its staged definitions; the other two left each pool's start method to the platform, which passed on macOS because macOS defaults to spawn. The fast tests pass with one more fault in place,definition_conformance.pyreadingscenarios.csvfrom the working tree, since those bytes equal the pin's. The fenced slow regeneration failed on it at an earlier head (mutant_slow_only.txt), and nobody has run it at this head.Invariants
build_proposals.pyandengine_probe.pywhole and each script's staging and reading in-process;benchmark_specs.json, not the checkout's. A test puts a copy of the package with a changed mapping first on the path, and the lookup doesn't change.use_staged_specsrefuses a process that has already loaded other definitions. A test runs each engine-side script's staging from the package's own definitions and checks thatpolicybenchthen reads the copy that script staged.definition_conformance.jsonrecord._identical_butrequires the same key, indentation and punctuation on those lines, and requires the newscript_sha256to equal the current script's hash.build_proposals.pycreates its output exclusively and refusesproposed_changes.jsonby path, so it writes neither through a hard link nor to another checkout's copy. The tests link a copy of the file, so a regression can't write the committed one.pass_inputs.pyholds a sha256 or names the run's path. The README tables list each pin with the scripts that stage it, no more and no fewer, and a test checks them by recording which pins each script stages.Review
subfleet run --task review --tier hard, atb4f2aac8) requested changes for two blockers and five other findings (review_sol.md).f6774fd1addresses all seven, andresponse_r1.mdmaps each finding to its fix and tests.e4d7e6e0, PR comment 6583944742) requested changes for four findings, and19482a56fixes all four:proposed_changes.json, so a regression would overwrite it;benchmark_specs.json(a nit).a248bc67and5b359ce6strengthen two tests after a mutation run found three faults they let through.~/reviews/policybench-reference-adversary-2026-10/other-scripts-pinned/holds everything cited above.Follow-ups
The Louisiana, Medicare Part B and payroll audits' impact scripts had the same working-tree pattern; #206 pinned them. Their engine-side sweeps still build
latest_finalfrom the working tree's convention modules and run under whatever engine the checkout installs. A separate PR can fix them, reusingpass_inputs.pyand the fence.axiom: n/a: audit tooling only; it encodes and changes no policy
🤖 Generated with Claude Code