Repository navigation
Conversation
A receipt names exactly what produced a published result. It records the installed wrapper, model and core versions; the data release and the SHA-256 of the simulated dataset bytes; the certificate digest, attached only when it covers that model and those bytes; reform and baseline digests bound to their representation; and the fiscal-year convention. Digests use RFC 8785 canonical JSON, so the dashboard and scorecard can recompute them. The JSON Schema ships in the wheel. Fixes #570 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ificate Independent review of #571 found ways a receipt could claim more than what ran. Reform digests no longer collide for Scenario.from_reform closures, lambdas, partials or bound methods: those are refused, and a module-level modifier is bound by source, bytecode and revision. Digests are over strict JSON, so numpy scalars, sets, Decimals and colliding keys are converted exactly or refused instead of stringified. canonical_json writes numpy floats and int subclasses as their values. Certification now requires the installed core version, records whether the certificate was packaged or caller-supplied, and verify_certificate checks it against a published manifest. Wheel hashes are recorded only when the builder verified the installed files against the wheel RECORD. The execution identity no longer depends on how the builder was called, reform and baseline are required, models parse strictly, overlay datasets get uncertified receipts, and v1 covers UK results. Refs #570 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- verify_certificate rebuilds the certificate from the bundle record with the builder's own derivation and compares every identity member plus the whole data identity; a forged receipt that keeps the genuine record digest is now reported. The "or require certificate_source packaged" rule is gone. - Modifier identity no longer digests marshalled bytecode or the Python version, so the digest is the same in any checkout. The source is recompiled and compared with the loaded code (ignoring file and line numbers), defaults and the module source are bound, and code_revision needs a full commit sha. - Wheel verification hashes every archive member against the wheel's own RECORD (one to one) and exempts only the launchers an installer generates for the wheel's declared console/GUI scripts; stray .pyc and INSTALLER files outside their installer locations fail. - The public metadata_module hook is removed; versions always come from importlib.metadata. - scenario_reform_identity refuses anything that is not a Scenario. - strict_json_value refuses numpy longdouble and deep nesting with ReceiptError; content_sha256 raises ReceiptError for unsafe integers. - The schema fixes package names and the UK fiscal-year convention as consts, and drops the never-populated release_manifest_sha256. Refs #570 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Before binding a modifier, the module's source as read now must match what was imported: every module-scope function in that file compiles to its loaded code, and every module-scope name bound once to a literal still holds that value. Edited helpers, edited constants and constants reassigned at run time are refused instead of named by the digest. - Functions defined in module-level blocks bind (source is dedented before compiling); methods are refused with a clear reason. - Unreadable wheels (not a zip, duplicate RECORD entries) raise ReceiptError from installed_package_identity. - Tests now kill the round-3 surviving mutants: each forgery alone, the record-version and basis checks, operator-only and nested-code edits, forged function globals and duplicated wheel members. Refs #570 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Wheel verification now checks bytecode: every __pycache__ file this interpreter would load for a verified source (any optimisation level, listed in RECORD or not, under sys.pycache_prefix) must compile from that source. Caches the interpreter would ignore are reported as inert. Launchers are exempt only when they exist in bin/Scripts outside the site directory. - The microsimulation adapter requires the file to still hash to the SHA-256 verified at load time, and the receipt to name the dataset, URI, revision and package recorded then; unmanaged loads cannot be receipted. - Data identity is resolved with the materializer's own resolver (overlays, per-dataset package and revision, release manifest revision), and a receipt never gives an artifact's name to other bytes. - Modifier binding: helper defaults, block-scoped literals and comprehension scope are checked; every module function's defaults are bound with their types; foreign helper globals and conditionally defined functions with different code are refused. - Only .whl direct-URL archive hashes are recorded as wheel_sha256. - Masked, 0-d, complex, datetime and structured NumPy values are refused. - Years must be integers and schema_version may not be a boolean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been 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.
Fixes #570
What this adds
policyengine.provenance.receipt.ExecutionReceipt(schema v1) is a strict, immutable, JSON round-trippable record of exactly what produced a UK result. It is the contract the UK Budget dashboard (W5) uses for per-measure receipts, and the scorecard (W6) binds to it. Both need it by 14 October.wrapper,model,corepolicyengine,policyengine-ukandpolicyengine-core, read from the installed distributions. The schema fixes the names as consts. A wheel SHA-256 appears only with evidence: eitherwheel_paths=(the builder checks the wheel's members against its RECORD one to one, then the installed files against the wheel), or the installer's PEP 610 archive hash.datacertification_status,certificatecertifiedonly when the bundle certificate covers the installed model and core, those dataset bytes and name, and the running wrapper. Otherwise the receipt isuncertified. The certificate digest is RFC 8785 overdata_releases.uk.reform,baselinecurrent_lawor a digest of{representation, payload}, never normalized across representations. AScenariowith a module-levelsimulation_modifieris bound bycode_revision(see below).fiscal_yearuk_fiscal_year: year Y is the fiscal year starting 6 April Y (2027-28). It is a const in v1.outputs_sha256,runtimeAlso added:
provenance.canonical(RFC 8785);provenance.wheels;build_execution_receiptandbuild_execution_receipt_for_microsimulation;verify_certificate;x-policyengine-rulesandx-policyengine-digests;docs/receipts.md.Invariants (each tested)
verify_certificate(receipt, bundle)rebuilds the certificate and the data identity from the bundle record, using the builder's own derivation. Any consistent forgery of a certified member is reported, even one that keeps the genuine record digest. Tests cover each of 16 single-field forgeries, Hypothesis-generated combinations, and a builder/verifier differential over bundle edits.certificate_sourceis the producer's statement, not evidence.Decimal, arrays, NaN, datetimes, longdouble and deep nesting are refused withReceiptError.rfc8785package (differential) and the W6 golden fixtures byte for byte.code_revisionwith a full commit sha.entry_points.txtare exempt, and stray.pycorINSTALLERfiles fail. The real policyengine-core 3.33.0 install verifies: 199 members and 199 files.Review history
Independent adversarial review by an Opus 5.5 agent; the Codex lanes were unavailable. Each round executed reproductions and mutation runs.
Breaking against earlier heads (v1 is unreleased):
metadata_module=is removed;release_manifest_sha256is removed fromdata;ModelIdentityandCoreIdentity.Re-vendor from this head.
Checks run locally
generate_receipt_schemas.py --checkpasses, and the schema output is identical on pydantic 2.10.6 and 2.14.0.axiom: n/a: provenance/infrastructure contract, no policy change.
🤖 Generated with Claude Code