Repository navigation
Conversation
eval-no-tools-chunked and retry-failed-responses start one eval-no-tools subprocess per chunk, and each imported policyengine to build its resume sidecar's policyengine_bundles. Apply the supervised-run handoff (#181): - PolicyEngineProvenanceHandoff writes <output_dir>/policyengine_provenance.json from a fresh interpreter (the workers' sys.executable and env) the first time a chunk needs to run, and every chunk subprocess gets POLICYBENCH_POLICYENGINE_PROVENANCE. It is thread-safe and shared by every model of an invocation; a retry round writes one file at its root. A pass with no pending chunks writes nothing. - Same guards as the supervisor: single-country only, the writer's timeout, read-back before handing the file on, and an inherited variable dropped. Unlike the supervisor it does not delete an earlier file first, because the runbook runs several invocations on one output dir. - The writer command, timeout, subprocess call and read-back move into policyengine_runtime so the supervisor and chunked paths run the same code. - The sitecustomize probe moves to tests/policyengine_probe.py, shared by the supervisor's end-to-end test and new chunked and retry ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
… fake answers are accepted The probe's fake explanations have no `value = X` trailer, so _enforce_explanation_value_contract drops them and the retry merge rejects every group as missing an explanation. That is the fake, not the handoff. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Runbook and handoff docstring: invocations replace the file atomically instead of deleting it first, but a writer whose own check fails removes whatever file is there; a worker that finds it missing or foreign recomputes. Drop the Section 6 merge pass as a no-pending example (it passes no --model), and note that a later retry round's merged output overwrites its copy of the source predictions. - Test that a valid file left by another invocation is not handed on when this invocation's writer fails. - The end-to-end probe check also requires every unclassified probed process to be free of PolicyEngine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 30, 2026
Contributor
Author
|
Heads-up: #186 overlaps this PR in
In the pinned environment the bundles are byte-identical to main's, and |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
eval-no-tools-chunkedandretry-failed-responsesstart onepython -m policybench.cli eval-no-toolssubprocess per chunk (up to--parallelat a time, per model). Before this PR, each chunk worker built its resume sidecar'spolicyengine_bundlesitself. For US chunks that meansimport policyengine, the cost #181 removed from supervised workers. This PR applies the same handoff to the chunked and retry paths. It is the third follow-up listed in #181.What a chunk worker was doing (read and probed this session)
run_chunkspawnseval-no-tools --num-scenarios --country --program-set --scenario-manifest --scenario-start --scenario-end --model -o [--no-explanations] [--single-output]. The subprocess inheritedos.environ, with no provenance file._build_resume_metadata, thenresolve_policyengine_bundles(falling back to computing), thenpolicyengine_bundles_for_countries, then_load_policyengine_manifest. It happens once per worker, before the first LLM request, under both runners, with or without explanations, for every program set.run_chunkargv (fake LLM, heavy imports blocked and recorded) covered 28 combinations: us/uk × every program set × batch/single-output × explanations on/off. With a provenance file, no combination loaded or attempted a heavy module.import policyengine. UK workers in this lockfile did not, because policyengine pins policyengine-uk 2.88.x and 2.89.0 is installed. That depends on the environment, so the UK path is handed the file too._load_eval_scenariosrejects any manifest scenario whose country differs from--country. Every chunk that reaches its sidecar is therefore single-country, which Compute PolicyEngine provenance once per supervised run #181's single-country guard requires.Change
PolicyEngineProvenanceHandoff(policybench/chunked_eval.py). The firstworker_env(countries)call writes<output_dir>/policyengine_provenance.jsonfrom a fresh interpreter, using the workers'sys.executableand environment. It reads the file back and, only if that succeeds, returns an env withPOLICYBENCH_POLICYENGINE_PROVENANCE. It holds a lock, so model threads share one write; a failure is not retried per chunk.run_chunked_evalcreates one handoff and passes it to everyrun_model_chunks.run_retry_modelscreates one at the round's root rather than one permodel_runs/<model>.run_model_chunkscalled on its own creates its own.POLICYBENCH_POLICYENGINE_PROVENANCEis dropped;--output-dir "$RUN_DIR/$country". The writer replaces the file atomically (pid-named temp file plusos.replace), and this invocation hands the path on only after its own writer succeeded. Another invocation can still replace or remove the file while this one's workers run: Compute PolicyEngine provenance once per supervised run #181's writer removes whatever is at the path when its own round-trip check fails. A worker that then finds the file missing, or written for a different environment, computes the bundles itself. That is safe, but it loses the speedup.PROVENANCE_WRITER,PROVENANCE_WRITER_TIMEOUT_SECONDS,run_policyengine_provenance_writerandread_written_policyengine_bundlesmove intopolicyengine_runtime.py. The supervisor's_compute_policyengine_provenanceand_write_policyengine_provenancenow call them. Supervisor behavior is unchanged, and its tests pass unmodified apart from using the shared probe.run_chunktakesenv=and passes it tosubprocess.run. With no env, the chunk inheritsos.environas before.RESUME_METADATA_VERSION, and chunk completeness and merging.chunk_is_completenever reads sidecars, and adding that check would make resuming chunks written under older packages fail. Workers that fall back printPolicyEngine provenance file not reusedto the invocation's terminal; nothing counts them, since chunk output isn't captured.Output-dir scanners (audited this session)
Nothing that scans chunked or retry output dirs picks up the new file. Every scan falls into one of these groups:
by_model/*.csvonly:full_run_export.load_predictions,reparse_predictions.reparse_country_run.runstore.import_run_dir:predictions.csv[.gz],scenarios.csv[.meta.json];_available_countriesandexport_country;fold_board's five named reference files;freeze_snapshot's explicit copy list.supervisor._validate_resume(scenarios/only),load_repeated_predictions(*.csv).The digests (reference CSV sha, runstore
source_csv_sha256,retry_metadata.json, snapshot manifest) cover only named files.test_output_dir_scanners_ignore_the_provenance_filerunsload_predictions,reparse_country_runandimport_run_diron a chunked dir with and without the file. It asserts identical results and that the file is left byte-for-byte unchanged.Invariants
os.environminus any inherited provenance variable.policyengine,policyengine_core,policyengine_us,policyengine_ukorh5py, checked in real subprocesses at every LLM request and at exit. The one-shot writer does load them (positive control).run_chunkcommand writes with no file (the pre-change behavior). That baseline worker loadspolicyengine_usbefore its first request, which is the positive control that the probe sees PolicyEngine in a worker.Invariants 1–3 are checked exhaustively rather than sampled. The test enumerates every request sequence of length 1–3 over
{us},{uk},{us, uk}and{US}, crossed with 3 writer outcomes and with and without an inherited variable: 504 cases.Tests
tests/test_chunked_eval.py:run_model_chunksat--parallel1 and 3; no write when nothing is pending;run_chunked_evalat--model-parallel1 and 2 writes once for two models;run_chunkfakes (they now takeenv).tests/test_retry_eval.py:run_retry_modelswith two models and--model-parallel 2writes once, at the round root, and no file lands undermodel_runs/.tests/policyengine_probe.pysitecustomize):test_chunked_run_keeps_policyengine_out_of_chunk_workers:eval-no-tools-chunkedwith 2 Claude models × 2 chunks. It checks one writer, a clean orchestrator, clean workers and no other probed process loading PolicyEngine, plus byte-identical sidecars against the baseline worker.test_retry_round_keeps_policyengine_out_of_chunk_workers:retry-failed-responseswith 2 models: one file at the root, and all 4 model-household responses retried with predictions.run_chunkdropping env, a per-model handoff in retry, a per-model handoff in chunked, and skipping read-back. The light tests caught all 9.ruff check,ruff format --check, and the non-PolicyEngine tests intest_chunked_eval.pyandtest_retry_eval.py(46 passed). The host is memory-constrained right now, so the PolicyEngine-importing tests (the three end-to-end tests,test_supervisor.py,test_policyengine_runtime.py) run in CI here, not locally.uv run pytest -m "not slow"passed the whole suite, including both new end-to-end tests. The first CI run failed only on the retry test's last assertion, which expected the merge to accept the probe's fake answers. The fake's explanations have novalue = Xtrailer, so_enforce_explanation_value_contractdrops them and the merge rejects every group. The test now asserts that every model-household response was retried with predictions.Adversarial review (three lenses, each finding put through a refutation pass)
--model);OSErroraborts the run": every spawn in the codebase already behaves this way, including run_chunk's own next spawn and the supervisor's writer on main, and no data is lost.Not measured
I have not timed chunked runs before and after. #181 measured the same
eval-no-toolsworker path under the supervisor: about 1 GB peak RSS and 11–14 CPU-seconds per worker for the PolicyEngine import. A chunk worker pays that once per chunk (--chunk-sizescenarios, 50 by default and 10 for retries), not once per scenario.axiom: n/a: benchmark runner infrastructure (no policy change)
🤖 Generated with Claude Code