Repository navigation
Conversation
The executor bundle, routing state and public PolicyEngineBundle now carry the certified default dataset SHA-256 from the installed policyengine.py bundle, and /versions reports each deployed wrapper version's bundle identity. The deploy smoke can assert that latest routes to the deployed policyengine version with its UK model and dataset digest, on the candidate and after promotion. The policyengine-release updater verifies the dispatch's bundle identity against the installed wheel before opening its PR and records the digests there. Legacy job bodies stay byte-identical: the new field is omitted when unset. Fixes #742 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review findings on #743: - H1: /versions route maps hold versions as latest and app names under each version key. The smoke now checks the release's own route and UK bundle identity, that its UK model routes to the same app, its dataset digest, and, with EXPECTED_LATEST=true, that latest routes to that version and model. Its test fixture is now the gateway's real /versions output, kept current by a gateway differential test. - M1: one historical bundle with an unresolvable dataset is reported without a dataset instead of failing /versions. - M2: tests pin the EXPECTED_* wiring, prepare's dataset output and the updater's RELEASE_PAYLOAD. - M3: verify-dispatch fails closed when the installed bundle cannot report a field the dispatch names (except the certificate digest), and reports dispatch_verified only when something was compared. - policyengine.py sends data_release_version as the data package version; compare it with a new uk_data_package_version output (the record's build-style version never matched). A differential test derives a real dispatch from the locked bundle and verifies it. - Qualification fields are validated; a pair marked not promotable, or with a verdict that is never promoted, is refused. Digests compare case-insensitively and malformed bundle digests are refused. - Docs: /versions reports the registry's identity for each bundle; the numerical sentinel (#744) checks what the worker computes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The deploy smoke compares both the policyengine route and its UK model route with the executor app just deployed (EXPECTED_APP_NAME), so a release whose routes still point at the old app fails. - Identity expectations are all or nothing: EXPECTED_IDENTITY=required (existing-stack deployments) demands the version, app, UK model and a well-formed dataset digest, and setting any one demands the rest. - The updater inspects the target bundle and verifies the release dispatch before it opens any PR, including for a branch left by an earlier run, and checks that branch pins the target version. - Dataset digests use strict fullmatch hex validation in the gateway and executor (no trailing newline, sign or underscores). - Tests parse the active workflow YAML (PyYAML in the entry test extra) and cover the surviving mutants: wrapper equality, certificate comparison, historical URIs and hex validation. 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 #742
Part of the UK Autumn Budget CI plan (workstream W4: qualification, promotion and receipts; W4 owns the sim-api deploy and its rollback). Production readiness means three things:
This PR does (1). #744, stacked on this one, adds (2) and (3).
Changes
Executor bundle (
release_bundle.py).CountryReleaseBundle.default_dataset_sha256holds the certified SHA-256 of the default dataset, read from the installed bundle in this order:Another dataset's hash is never borrowed, and malformed values are dropped.
Routing state (
update_version_registry.py) recordsdefault_dataset_sha256per country. The key is optional, so older readers ignore it.Public schema (
gateway_models.py):PolicyEngineBundle.dataset_sha256is optional and must be 64 lowercase hex characters.VersionsResponse.bundlesholds the identity of each deployed wrapper version, keyed by wrapper version and then country. It defaults to{}.Gateway (
endpoints.py):dataset_sha256for the app's default dataset, which is whatdatasetnames./versionsreportsbundles.dataset_sha256when it is unset, so legacy bodies stay byte-identical. This is the rule_bundle_payloadalready applies tospm.Deploy smoke (
cloud-run-simulation-entry-smoke.sh). In/versions, each route map holds a version underlatestand an app name under each version key. Optional expectations make it prove that:EXPECTED_POLICYENGINE_VERSION: that version has a route and a recorded UK bundle identity;EXPECTED_UK_MODEL_VERSION: that bundle records this UK model, and the model routes to the same app;EXPECTED_UK_DATASET_SHA256: the bundle's dataset digest matches;EXPECTED_LATEST=true:latestroutes to that version and model.With these unset, behaviour is unchanged. This is the identity the gateway reports from the routing registry; Gate releases on a numerical sentinel and make them reversible #744's numerical sentinel checks what the worker computes.
Deploy wiring (
simulation-deploy.reusable.yml):prepareexportsuk_dataset_sha256from the executor's locked bundle.promote_entrypointnow needsprepare.Updater (
check-policyengine-updates.yml, the update script and its helper):verify-dispatchhelper command fails the update when the dispatch'sbundle.uk(model, core, data package version, dataset SHA-256, certificate digest) differs from the installed wheel's. It fails closed: an installed bundle that cannot report a named field is a mismatch, except for the certificate digest, which older wrappers cannot compute.dispatch_verifiedis true only when at least one field was compared.promotable: false, or with a verdict that is never promoted, is refused. The verdict and receipt digest are validated before they are printed.versionkeep working.The policyengine.py side of the payload is policyengine.py#574 (the release dispatch). The certificate digest is the receipt-v1 digest from policyengine.py#571; older wrappers report none, and the updater skips it for them.
Invariants
dataset_sha256is reported only for the dataset the bundle names as default. It is never a hash of a different dataset. Tests cover the certified, fallback, other-dataset, malformed and manifest-fallback cases.test_spm_routesset, and a new test covers a worker that has the digest.latestroutes elsewhere. It passes with no expectations set. A stub-curltest covers each case. The/versionsbody in those tests is the gateway's real output: a gateway test regenerates it and fails if the committed fixture drifts (differential).release_bundle_identitydoes, and checks that it verifies./versionsstays up. One historical bundle with an unresolvable dataset is reported without a dataset; it does not fail the response.Review
Round 1 (an Opus 5.5 agent, with reproductions and mutation runs) asked for changes; all are fixed in bfe10b4.
latest, which is a version, with an app name, so every correct deploy would have failed. My test fixture had the same wrong shape, which is why the tests passed./versionsreturned 500 on one bad historical bundle; nothing pinned the workflow wiring;verify-dispatchpassed when the installed value was empty.data_release_versionwas compared with the record's build-style version, so every real dispatch would have failed.Round 2 (GPT-6.1 Sol) is running.
Tests
ruff format --checkpasses for CI's lint scope.Not in this PR
latestwhen a run fails afterupdate_routing.latestonly when the release should advance it. Until then, an older wrapper deployed withoutforce_latestfails this smoke.These are in #744.
🤖 Generated with Claude Code