Repository navigation
Fingerprint policyengine.py 6.x's bundle manifest in PolicyEngine provenance - #185
Merged
Merged
Conversation
…venance
policyengine_provenance_inputs() hashed only policyengine's per-country
release manifests (data/release_manifests/{country}.json). policyengine.py
6.1.2 ships none; it ships one data/bundle/manifest.json, which
_load_raw_policyengine_manifest (and policyengine's own get_release_manifest)
read each country's release from under data_releases. Under the pinned 6.1.2
the fingerprint recorded {'us': None, 'uk': None}, so an in-place edit of the
bundle manifest between the provenance writer and a worker went undetected.
The pre-merge delta review of #182 showed it: editing data_releases.us.bundle_id
left the fingerprint unchanged.
The fingerprint now also records bundle_manifest_sha256 (None when the file is
absent). Both manifest paths are module constants shared by the reader and the
fingerprint, so they cannot drift apart. The per-country layout (4.16.1)
fingerprints as before.
The supervisor's module docstring said computing provenance imports
policyengine and builds the US and UK tax-benefit systems. That holds only
when a country's installed model is the exact version policyengine.py pins
(policyengine-us 1.723.0 under 4.16.1: re-measured 11.1 CPU-s, 909 MB max RSS,
policyengine_us.system and policyengine_uk.system loaded). With 6.1.2 and
policyengine-us 2.15.17 (6.1.2 pins 2.2.1) it loads no PolicyEngine module
and reads only package metadata and the bundle manifest. The docstring now
describes both cases; computing once per run still bounds the cost in the
first.
Tests: the reviewer's in-place edit, run for each layout, makes a written
provenance file stale and the worker recompute the edited release; each
layout's manifest hashes; the installed policyengine.py's manifest is hashed;
and a Hypothesis property that, across any mix of the two layouts' files,
fingerprints are equal exactly when the files are byte-identical, so equal
fingerprints hand every country the same raw release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…est coverage From the independent Opus review of #185 (APPROVE, nothing blocking): - supervisor.py: the 4.16.1 import built both country systems only with POLICYENGINE_SKIP_COUNTRY_IMPORTS unset (policyengine/__init__.py gates each country import on it); say so. - docs/runbook.md: workers read the provenance file "instead of computing it", not "instead of importing policyengine", since under policyengine.py 6.1.2 plus policyengine-us 2.15.17 computing it imports nothing either. - tests: note that the manifest paths are spelled out on purpose, and that the property test covers both layouts' files, which is all the raw reader opens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the in-session execution verifier and delta reviewer of #185 (both APPROVE, nothing blocking): - A fingerprint that hashed the bundle manifest only when no per-country file existed survived some Hypothesis seeds (for example --hypothesis-seed=5), because no deterministic test had both layouts present. policyengine.py 6.1.2's own loader reads the bundle manifest even then. An explicit @example now covers it. - The strategy varied only data_releases, so hashing json.dumps(data_releases) instead of the file was caught only by the exact-hash test. Bundles may now carry dataset_overlays, which 6.1.2's loader applies, and an @example pins it. - The relocated_policyengine docstring said _load_policyengine_manifest "returns None" when no model matches its pin; it is not called then. Reword. - The in-place edit is credited to the pre-merge delta review of #182, which was not posted on GitHub. - test_provenance_file_reproduces_direct_computation ids came from str(set), which varies with PYTHONHASHSEED, so a node id collected in one process was not found in the next. Ids are now the sorted country codes. Both mutants are caught on seeds 1 and 5: the mixed-layout one by the property test, the data_releases-only one by it and the exact-hash test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
Review record before mergeIndependent reviews: all APPROVE, nothing blocking
Follow-ups applied
Known limits, unchanged by this PR and not reachable under the current pins
CI on 🤖 Generated with Claude Code |
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
Fixes two findings from the pre-merge delta review of #182 (merged as d616e67).
A. The provenance fingerprint missed policyengine.py 6.x's bundle manifest.
policyengine_provenance_inputs()hashed onlypolicyengine/data/release_manifests/{country}.json. policyengine.py 6.1.2 ships none of those. It ships onepolicyengine/data/bundle/manifest.json, which_load_raw_policyengine_manifestfalls back to (data_releases.<country>), and which policyengine's ownget_release_manifestalso reads in 6.1.2. Under the pinned env the fingerprint recorded{'us': None, 'uk': None}, so an in-place edit of the bundle manifest between the provenance writer and a worker went undetected. The fingerprint now also recordsbundle_manifest_sha256, which is None when the file is absent. Both manifest paths are now module constants that the reader and the fingerprint share, so they can't drift apart again. The docstring no longer claims the manifests are "recorded directly" without qualification. It lists what is recorded, in either layout.B. The supervisor docstring overstated the cost of computing provenance. It said computing provenance "imports policyengine, which builds the US and UK tax-benefit systems". That happens only when a country's installed model is the exact version policyengine.py pins. The docstring now describes both cases. Computing once per run still bounds the cost when the import does happen.
Verified this session
site-packages/policyengine/dataholds onlybundle/manifest.jsonandschemas/. Before the fix:release_manifest_sha256 == {'uk': None, 'us': None}.data/release_manifests/{us,uk}.json, and nobundle/manifest.json(itsdata/bundle/holdsbundle.json). Itsget_release_manifestreads the per-country files.d7e6308e…):data_releases.us.bundle_idin place.PolicyEngine provenance file not reused (…): it was written for different PolicyEngine packages or codeand resolvesus-6.1.2-EDITED.uv run python -c "import sys; from policybench.policyengine_runtime import policyengine_bundles_for_countries as f; f({'us'}); print([m for m in sys.modules if m.startswith('policyengine')])"prints[], and so does{'us','uk'}. policyengine 6.1.2 pins policyengine-us 2.2.1 (2.15.17 installed) and policyengine-uk 2.90.2 (2.89.0 installed). The versions differ, so_load_policyengine_manifestis never called.policyengine,policyengine_core,policyengine_us.systemandpolicyengine_uk.system, and took 11.1 CPU-s at 909 MB max RSS. That matches the dated 2026-09-28 figure kept in the docstring.Tests (
tests/test_policyengine_runtime.py)test_manifest_edited_in_place_makes_the_provenance_file_stale[per-country|bundle]: the reviewer's in-placebundle_idedit, run on a fake layout built from the existingtest_raw_manifest_reads_the_policyengine_6_bundle_layoutpattern._read_policyengine_provenancerefuses the stale file, andresolve_policyengine_bundlesrecomputes the edited release.test_provenance_inputs_hash_each_layouts_manifests[per-country|bundle]: the per-country layout records each file's sha256 as before, withbundle_manifest_sha256 = None. The bundle layout records the bundle hash, with None per country.test_installed_policyengine_manifest_is_fingerprinted: the pinned policyengine.py's manifest is actually hashed.test_equal_fingerprints_mean_equal_raw_manifests(Hypothesis) covers any mix of the two layouts' files, each present or absent.Invariants
Mutation check: against the old
policyengine_runtime.py, the bundle-layout stale test and the property test fail on every seed tried. The per-country tests fail only on the new key; their per-country hashes still match.Gates run locally
uv run ruff check .: passes.uv run ruff format --check .: passes.uv run pytest -q -m "not slow": 1056 passed, 5 skipped, 13 deselected.Interaction with open PRs
#184 (
chunked-provenance-handoff) also editspolicyengine_runtime.pyandsupervisor.py, and both auto-merge with this branch. #184 already conflicts withmainintests/test_supervisor.py, because #182 changed that file after #184 branched. This PR doesn't touch that file.axiom: n/a: provenance fingerprint and docstring; no policy change.
🤖 Generated with Claude Code