Repository navigation
Automate the results PR for a certified UK replay qualification - #193
Merged
Merged
Conversation
When the receiver records a complete request, its record job dispatches the new uk-replay-results workflow. That job reads the request from replay-state and, for a certified VARIANT.json, builds the results pull request the way #188 and #190 were built by hand: the results and requirements trees byte for byte, the variant record plus a note in canonical form, the CI matrix entry, the NEWEST/COMMITTED test constants and a RELEASE_REPLAY.md paragraph from the receipt's counts. It runs verify-sealed, CI's regeneration and two database builds, pushes its own branch, opens the PR and dispatches CI on it (a GITHUB_TOKEN PR starts no pull_request run). It holds no dataset token, runs no candidate or release code and never merges. The discovery floor tests now read the index as of 6.5.0, so appending a certified variant no longer moves their reference point. WIP: builder tests and the real-data differential run follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The bare-remote run caught check() resolving the results tree one level too deep (results/results/..., docs/docs/...); fixed. Pins the workflow's trigger, permissions and branch condition, the record job's dispatch, CI's workflow_dispatch with the token still push-only, and reproduces the hand-built 6.4.1 note, the index and CI matrix appends and the 6.5.0 paragraph's integrity sentence. Hypothesis properties cover the index, matrix, paragraph counts and commit scope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two facts made the GITHUB_TOKEN design unworkable. The bot's commit edits ci.yml's matrix, and only a token with workflows permission may push a change to a workflow file; GITHUB_TOKEN can never hold it. And checks from a workflow_dispatch CI run do not count for a pull request (GitHub's required-status-checks troubleshooting page), so dispatching CI on the branch would have left the PR with no checks for gh pr checks. The job now mints a token for the organisation's policyengine App (the pattern policyengine-us and policyengine-api use), scoped to this repository with contents, pull-requests and workflows write; GITHUB_TOKEN reads only. The App's push and PR start ordinary pull_request CI, so the ci.yml workflow_dispatch trigger and the CI dispatch are gone. Adds a test that a sealed file under a path main's .gitignore ignores fails the byte-identity check rather than being silently dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split the builder into build and propose. The build step holds no write credential (contents: read, checkout credentials not persisted) while it runs the checks and their PyPI packages. Only a verified commit gets the App token, and the propose step, which runs git, gh and the standard library alone, pushes exactly the commit build reported, on the main it was verified on. check() now skips an exactly certified pair qualified again (the receiver returns the committed record, note included) instead of refusing it, refuses a variant id that cannot name a branch and paths, requires the freeze under docs/uk_replay/requirements/, validates the record against the results tree and requires the diff's base to be committed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The paragraph would have said "flags one rows" for a single flagged row; counted() picks the singular, and a property now forbids either mismatch. QUALIFICATION_INTERFACE.md says a certified run's results reach main only through its results pull request, so the latest_certified base moves when that PR merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI's interpreter loads setuptools' _distutils_hack and a zope namespace at startup through .pth hooks; the probe now subtracts the modules present before the builder is imported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of 97c351b, blocking 1: steps in one job share a runner, so a compromised PyPI dependency in the build step could reach the token step (a GITHUB_PATH git shim catching GH_TOKEN; the token's workflows: write could then exfiltrate the App's org-wide key). The propose job now runs on a fresh runner with no uv: it assembles the same commit from the same main and replay-state commits with git and python3 -I -S, refuses unless its tree is the one the build job verified (and the commits are in each branch's history), and only then mints the App token. Its actions are pinned to commits (checkout v4.4.0, create-github-app-token v2.2.2). check and apply take --stdlib to skip the jsonschema, rfc8785 and yaml checks the build job made; the builder keeps its own canonical_bytes, tested equal to compute_uk_event's. Blocking 2: a rerun force-pushes only over exactly one commit authored and committed by the bot with this request's trailer; an amended commit keeps its author but not its committer, so it now counts as a person's. Also: an id main holds for another identity is refused, not skipped; grammar in the PR-body table; doc wording on attribution and on an older release refused for good. Tests: the two-job harness (job 2 on a fresh clone under python3 -I -S), forged job-1 outputs, main moving between the jobs, the amended commit, a push racing the force-with-lease, the gh credential helper, the id collision, the recorder dispatching nothing for a failed request, and the canonical-form differential. Local test runs held for Fleet ops' disk pause; CI runs the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Delta review of f7247f1: - Blocking 2: the identity check compared main's record, which carries the note the results PR added, with VARIANT.json, which never has one, so rerunning a merged request failed instead of skipping. Compare them without the note. - Blocking 1: three test bugs (a prose match in the propose pin, a probe made vacuous by the lazy imports, a stub dir that collided with the remote fixture). The control-environment pin now scans every import of the strict path statically; CONTROL drops numpy, which nothing it runs imports. - Non-blocking 3: rebuild refuses a base older than the workflow's own commit and decides again against main's current tip, so job 2 rerun on stale outputs after the merge skips instead of opening a second PR. - The credential test also runs the real gh where installed; a test pins the stdlib matrix edit to the strict one on the real ci.yml for 6.4.1 and 6.5.0; doc wording on what the tree check guarantees. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
October 18, 2026 19:42
MaxGhenis
added a commit
that referenced
this pull request
Oct 18, 2026
Review of #193 at 78e080c, finding 1: rebuild's tip re-check extracted the request from replay-state into its own directory and ran `builder check` on it before `assemble` had run the output guard, so it read unguarded JSON (through any symlink), and a missing request failed with a raw git archive error. The tip check now runs after assemble and the tree comparison, on $STAGE/$REQUEST, the copy the guard has already checked. The order pin names it, and rebuild may hold no other extraction. Also drops the orphan "# scoped to" comment line in uk-replay-results.yml (finding 6) and reads JSON fields through one helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 18, 2026
Review of #193 at 78e080c, finding 2: the id-collision check compared the committed record with VARIANT.json field by field (without the note), so a later change to main's copy (a schema migration adding a field) would turn a merged request's rerun into the misleading "qualify this pair again" refusal. A request whose variant id main holds is now skipped exactly when the id and identity_sha256 agree and main's record is certified; the rest of the record is not compared. Because the skip rests on the digest, VARIANT.json's identity_sha256 must be its identity's. Tests in both directions, strict and stdlib: a migrated record (note, added, dropped and reshaped fields) skips, and a Hypothesis property extends that to any change outside the compared fields; another identity, another digest, a non-certified record and a digest that is not the identity's are refused, with a property over every identity leaf. The end-to-end skip test also migrates main's record after the merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 18, 2026
Review of #193 at 78e080c, finding 3. The strict == lean matrix test held by construction: add_matrix_variant builds the edited text once and both modes return it. Replaced with tests of what the propose job relies on: - tests/fixtures/uk_replay/results_pr holds the ci.yml #188 and #190 actually edited (e796ffb^ and f4164f2^, pinned by their git blob ids, since CI's shallow checkout has no history). With yaml unimportable, the stdlib edit of #190's input is #190's ci.yml blob (29f7a1b) exactly, and of #188's input is #188's matrix hunk alone (#188 also changed the determinism step). The strict mode raises ImportError there, so the block bites. - The same edit under python3 -I -S, the propose job's interpreter. - A Hypothesis property: on the real files, the stdlib list check alone refuses a matrix listing anything but the certified variants before the new one. The propose pin no longer skips every line containing ::error::: only the text of a quoted ::error::/::notice::/::warning:: string with no command substitution is set aside, propose may hold no inline comment, and the pin checks its own rule on a builder call after a message, inside one and after a #. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 18, 2026
…lone Review of #193 at 78e080c, finding 4: the control-environment pin read the imports of four named files and ignored every pipeline.* import, so a new third-party import in another module those files reach would go unseen; and the test uv stub ran the test interpreter, numpy and all, so nothing in CI ran the build job's checks with CONTROL's packages. - ImportScan follows what runs from the strict entry points (the builder's main, and uk_replay_qualify main's verify-sealed branch): a module's top level when imported, a function or class when reachable code names it, directly or through a module alias, and an import statement only where reachable code holds it. Pipeline modules are followed however they are named, including relative and bare sibling imports; dynamic imports in reached code fail the pin. The reached third-party set must be exactly CONTROL's (jsonschema, rfc8785, pyyaml), no release package may be reached, and the reached modules and key functions are pinned. A synthetic package checks the scan itself: calls across modules, a class method, a relative and a sibling import count; an unreachable function, another command's branch and a __main__ block do not. - The test uv now runs `uv run [--with SPEC]... python -m ...` with every installed package refused except the specs and what they require (extras aside), so the end-to-end build jobs run the builder and verify-sealed with CONTROL's packages alone. Tests pin the emulation (on real modules: numpy, hypothesis and pytest refused, jsonschema's dependencies admitted) and run verify-sealed on every committed sealed namespace under it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 18, 2026
Review of #193 at 78e080c, finding 5: the harness stripped every GITHUB_* variable, so no end-to-end run set GITHUB_SHA to the dispatch-time main, the production case, and the "consistent older base" test forged only the base, so the tree comparison would also have refused it. - Remote.run is a dispatch: GITHUB_SHA is main as dispatched (or a given commit), and both jobs check that commit out, as actions/checkout does. build_job and rebuild_job run one job each for the tests that drive them separately, so those carry GITHUB_SHA too (a rerun keeps its run's). - A run dispatched before main moved proposes on the newer main, with the dispatch commit's code in both jobs. - A consistent older-base build: job 1 really built on the older main, so its tree matches. Rebuilt with that run's GITHUB_SHA it passes; with a later dispatch's it is refused by the GITHUB_SHA check alone. - Two behavioural pins for finding 1, with main moved so the tip check runs: a state commit without the request ends with "has no request", not a raw git error; and one whose STATUS.json is a link is refused by the output guard before any check reads it. Docs and the workflow comment state the new order: the tree comparison, then the decision on main's tip from the guarded request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 2026
MaxGhenis
added a commit
that referenced
this pull request
Oct 19, 2026
…der (#194) * Run the results PR's tip check on the guarded request Review of #193 at 78e080c, finding 1: rebuild's tip re-check extracted the request from replay-state into its own directory and ran `builder check` on it before `assemble` had run the output guard, so it read unguarded JSON (through any symlink), and a missing request failed with a raw git archive error. The tip check now runs after assemble and the tree comparison, on $STAGE/$REQUEST, the copy the guard has already checked. The order pin names it, and rebuild may hold no other extraction. Also drops the orphan "# scoped to" comment line in uk-replay-results.yml (finding 6) and reads JSON fields through one helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Compare a held variant's id and identity, not its whole record Review of #193 at 78e080c, finding 2: the id-collision check compared the committed record with VARIANT.json field by field (without the note), so a later change to main's copy (a schema migration adding a field) would turn a merged request's rerun into the misleading "qualify this pair again" refusal. A request whose variant id main holds is now skipped exactly when the id and identity_sha256 agree and main's record is certified; the rest of the record is not compared. Because the skip rests on the digest, VARIANT.json's identity_sha256 must be its identity's. Tests in both directions, strict and stdlib: a migrated record (note, added, dropped and reshaped fields) skips, and a Hypothesis property extends that to any change outside the compared fields; another identity, another digest, a non-certified record and a digest that is not the identity's are refused, with a property over every identity leaf. The end-to-end skip test also migrates main's record after the merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the stdlib matrix edit on the bytes #188 and #190 edited Review of #193 at 78e080c, finding 3. The strict == lean matrix test held by construction: add_matrix_variant builds the edited text once and both modes return it. Replaced with tests of what the propose job relies on: - tests/fixtures/uk_replay/results_pr holds the ci.yml #188 and #190 actually edited (e796ffb^ and f4164f2^, pinned by their git blob ids, since CI's shallow checkout has no history). With yaml unimportable, the stdlib edit of #190's input is #190's ci.yml blob (29f7a1b) exactly, and of #188's input is #188's matrix hunk alone (#188 also changed the determinism step). The strict mode raises ImportError there, so the block bites. - The same edit under python3 -I -S, the propose job's interpreter. - A Hypothesis property: on the real files, the stdlib list check alone refuses a matrix listing anything but the certified variants before the new one. The propose pin no longer skips every line containing ::error::: only the text of a quoted ::error::/::notice::/::warning:: string with no command substitution is set aside, propose may hold no inline comment, and the pin checks its own rule on a builder call after a message, inside one and after a #. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Follow the strict paths' imports, and run their checks with CONTROL alone Review of #193 at 78e080c, finding 4: the control-environment pin read the imports of four named files and ignored every pipeline.* import, so a new third-party import in another module those files reach would go unseen; and the test uv stub ran the test interpreter, numpy and all, so nothing in CI ran the build job's checks with CONTROL's packages. - ImportScan follows what runs from the strict entry points (the builder's main, and uk_replay_qualify main's verify-sealed branch): a module's top level when imported, a function or class when reachable code names it, directly or through a module alias, and an import statement only where reachable code holds it. Pipeline modules are followed however they are named, including relative and bare sibling imports; dynamic imports in reached code fail the pin. The reached third-party set must be exactly CONTROL's (jsonschema, rfc8785, pyyaml), no release package may be reached, and the reached modules and key functions are pinned. A synthetic package checks the scan itself: calls across modules, a class method, a relative and a sibling import count; an unreachable function, another command's branch and a __main__ block do not. - The test uv now runs `uv run [--with SPEC]... python -m ...` with every installed package refused except the specs and what they require (extras aside), so the end-to-end build jobs run the builder and verify-sealed with CONTROL's packages alone. Tests pin the emulation (on real modules: numpy, hypothesis and pytest refused, jsonschema's dependencies admitted) and run verify-sealed on every committed sealed namespace under it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run the end-to-end results jobs as dispatched, GITHUB_SHA and all Review of #193 at 78e080c, finding 5: the harness stripped every GITHUB_* variable, so no end-to-end run set GITHUB_SHA to the dispatch-time main, the production case, and the "consistent older base" test forged only the base, so the tree comparison would also have refused it. - Remote.run is a dispatch: GITHUB_SHA is main as dispatched (or a given commit), and both jobs check that commit out, as actions/checkout does. build_job and rebuild_job run one job each for the tests that drive them separately, so those carry GITHUB_SHA too (a rerun keeps its run's). - A run dispatched before main moved proposes on the newer main, with the dispatch commit's code in both jobs. - A consistent older-base build: job 1 really built on the older main, so its tree matches. Rebuilt with that run's GITHUB_SHA it passes; with a later dispatch's it is refused by the GITHUB_SHA check alone. - Two behavioural pins for finding 1, with main moved so the tip check runs: a state commit without the request ends with "has no request", not a raw git error; and one whose STATUS.json is a link is refused by the output guard before any check reads it. Docs and the workflow comment state the new order: the tree comparison, then the decision on main's tip from the guarded request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Close the review's residual gaps in the new test pins The hard review of c5c17db approved with three P3 limitations in the pins this PR adds: - The propose pin's message stripping ignored shell quoting: `echo ""::error::; builder check; : ""` hid a builder call. A message is now one whole double-quoted word (whitespace or line start before it, whitespace, `;` or line end after) holding no other quote, backslash, backtick or command substitution. That case and a single-quote variant are self-checks. - ImportScan kept one binding per name per module, so an import in a function nothing reaches could overwrite a live binding, and it missed a module used as a value and aliased import_module. Names now resolve as Python scopes them (module-level imports everywhere, a function's own imports only in it) and to every binding they may have; a pipeline module used as a value (assigned, passed, given to getattr) makes all its functions reachable; and dynamic imports are caught by name, attribute or from-import, however aliased. A second synthetic package pins each case. On the real entry points the reached functions, modules and the three packages are unchanged. - The uv emulation kept requirements behind false markers and could not refuse a module loaded at startup. It now evaluates markers with packaging (extras only where asked for), and drops every refused module already loaded (its own packaging, anything a .pth imported) so the next import is refused; the probe checks packaging is. Versions remain the suite's, as the comment says. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 19, 2026
The results pull request no longer edits ci.yml. A replay-variants job lists the certified variants of data/uk/replay_variants.json in index order (.github/scripts/uk_replay_engine_matrix.py, the standard library under the runner's python3 -I, from a sparse checkout of that script and the index), and replay-engine's matrix is that list. The lister refuses the whole matrix on a record it cannot use. The builder drops add_matrix_variant, its only yaml import and ci.yml from EDITED; apply needs no package in either job; scope refuses any path under .github/. The App token drops permission-workflows, the build job's CONTROL drops pyyaml, and propose explains a refused rebuild over a main that changed a workflow. Finding 4/6 of both reviews of #193. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 19, 2026
The discovery floor cases were written when 6.5.0 was the newest certified variant. #193 pinned the index they pass to candidates(), but discover() reads the committed index through variants.load_document(), so 25 cases failed on the first automated results PR (#195, 6.7.5): with 6.7.5 certified, the floor moved and 6.5.1 and 6.5.2 were no longer discoverable. An autouse fixture now hands discover() the same floor index; a test that needs another index still patches it itself. Checked against #195's index (6.7.5 appended): the old file fails 25 tests, this one passes all 127. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 2026
MaxGhenis
added a commit
that referenced
this pull request
Oct 19, 2026
The discovery floor cases were written when 6.5.0 was the newest certified variant. #193 pinned the index they pass to candidates(), but discover() reads the committed index through variants.load_document(), so 25 cases failed on the first automated results PR (#195, 6.7.5): with 6.7.5 certified, the floor moved and 6.5.1 and 6.5.2 were no longer discoverable. An autouse fixture now hands discover() the same floor index; a test that needs another index still patches it itself. Checked against #195's index (6.7.5 appended): the old file fails 25 tests, this one passes all 127. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 21, 2026
The discovery floor cases were written when 6.5.0 was the newest certified variant. #193 pinned the index they pass to candidates(), but discover() reads the committed index through variants.load_document(), so 25 cases failed on the first automated results PR (#195, 6.7.5): with 6.7.5 certified, the floor moved and 6.5.1 and 6.5.2 were no longer discoverable. An autouse fixture now hands discover() the same floor index; a test that needs another index still patches it itself. Checked against #195's index (6.7.5 appended): the old file fails 25 tests, this one passes all 127. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 21, 2026
…ts bot's workflows permission (#196) * Read CI's replay-engine matrix from the variant index The results pull request no longer edits ci.yml. A replay-variants job lists the certified variants of data/uk/replay_variants.json in index order (.github/scripts/uk_replay_engine_matrix.py, the standard library under the runner's python3 -I, from a sparse checkout of that script and the index), and replay-engine's matrix is that list. The lister refuses the whole matrix on a record it cannot use. The builder drops add_matrix_variant, its only yaml import and ci.yml from EDITED; apply needs no package in either job; scope refuses any path under .github/. The App token drops permission-workflows, the build job's CONTROL drops pyyaml, and propose explains a refused rebuild over a main that changed a workflow. Finding 4/6 of both reviews of #193. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Answer the review of 6866605: reverted workflow changes, id length The refused-rebuild hint now also names a workflow change that main made and then reverted: it checks every commit from the old tip, not just the tips. Variant ids are capped at 120 characters in the lister and the builder alike, so two ids joined by "__" (a release diff's directory) fit one 255-byte path component and the matrix output stays far under GitHub's 1 MB per job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read full history for the refused-rebuild hint Default path-limited history prunes a merge to a parent its paths match, so a workflow change and its revert made on a side branch and merged after main moved on were invisible to the hint. --full-history shows them; the test's stand-in for GitHub reads full history too. Answers the review of 34fba89. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Automate the results PR for a certified UK replay qualification
Step 4 of the per-release flow was manual. Someone committed the receiver's certified namespace, record, CI matrix entry and test updates by hand: #188 for 6.4.1 and #190 for 6.5.0. With this PR, the receiver opens that PR itself. Merging stays on the normal gates: CI and an independent review. Nothing auto-merges.
How it works
recordjob (.github/scripts/uk_replay_record.sh) writes terminal statuscomplete, it dispatches.github/workflows/uk-replay-results.ymlwith therequest_id. If that dispatch fails, the job warns and the queue continues. The workflow can also be run by hand. Its only trigger isworkflow_dispatch, so it never runs onpull_request. Its single job runs only on the default branch's code, and each request gets its own concurrency group..github/scripts/uk_replay_results_pr.shandpipeline/uk_replay_results_pr.py(check,apply,scope,body) take these steps in order:Read
requests/<rid>fromreplay-stateand pass it through the receiver's output guard again.Check eligibility. The request needs STATUS
complete, a receipt that validates and matches STATUS, aREQUEST.jsonthat hashes to the id, and aresults/VARIANT.jsonthat iscertified, is the receipt's variant and was built from this request. Failed, rejected and candidate runs, and variants already committed, end with a notice.Refuse anything inconsistent:
latest_certified);An exact re-qualification of a committed pair (W4's determinism check returns the committed record, note included) is skipped, not refused.
Extract
requests/<rid>/results/{results,docs}withgit archive | tar -x --strip-components=3.Append
VARIANT.jsonplus anoteto the index withcompute_uk_event.canonical_bytes.Add the matrix entry. After the edit, the matrix must list exactly the certified variants, and nothing else in
ci.ymlmay change.Move
NEWESTin the qualify and determinism tests and extendCOMMITTEDin the variants test.Add a RELEASE_REPLAY.md paragraph built only from receipt counts: integrity, grids and diff flags.
Commit, and refuse the commit if it touches any other path.
Check that every committed result file has the blob
replay-stateholds.Run
verify-sealed(the new namespace, then all of them) and CI'suk_replay_regenerate.sh, then build the database twice withscorecard_db.build_db: the content hashes must match and no tracked file may drift.Mint the App token (next bullet), then push
uk-replay-results/<variant_id>, force-pushing only over the builder's own commits for this request, and open the PR. It opens as a draft when integrity is notcomplete.buildruns steps 1–11. ItsGITHUB_TOKENiscontents: read, the checkout persists no credential, and it holds no secret. It outputs the verified tree, the main andreplay-statecommits it used, and two check results.proposeruns on a fresh runner, with no uv or installed package and its actions pinned to commits. It assembles the same commit again with git andpython3 -I -S(stdlib only) on the commitsbuildused, which must be in main's andreplay-state's history. It refuses unless its tree equalsbuild's. Only then does it mint a token for the organisation'spolicyengineGitHub App, withactions/create-github-app-token(v2.2.2, pinned) and the org secretsAPP_IDandAPP_PRIVATE_KEY. The token is scoped to this repository withcontents,pull-requestsandworkflowswrite, the same App policyengine-us's weekly lock PR and policyengine-api's bundle update use.$GITHUB_PATHshim, say) would reach the step holding the token.Why an App token, not
GITHUB_TOKENThe first commit here used
GITHUB_TOKENand dispatched CI on the branch. That design could not work, for two reasons:.github/workflows/ci.yml. A push that changes a workflow file needsworkflowspermission, whichGITHUB_TOKENcan never hold.GITHUB_TOKENstarts nopull_requestrun (docs). Checks from aworkflow_dispatchrun don't count for a PR (docs), sogh pr checkswould never have passed.gh api /orgs/PolicyEngine/installationsshows thepolicyengineApp installed on all repositories withcontents,pull_requestsandworkflowswrite. Its push and PR start ordinarypull_requestCI.Tests made editable, discovery made stable
tests/test_uk_replay_variants.py: the committed list is now oneCOMMITTEDtuple the builder appends to.tests/test_uk_replay_determinism.py:POST_EVIDENCEpins the first namespace sealed after Check processed replay values; revise AB2024 blended CGT for day-weighting engines #186, soNEWESTonly drives thebase_namespacecheck.tests/test_uk_replay_discover.py: the floor cases assumed 6.5.0 was the newest certified variant, and appending 6.6.0 would have broken eleven of them. They now read the index as of 6.5.0 (floor_document()). Their assertions are unchanged.Invariants
Each has deterministic cases, and Hypothesis properties where it ranges over inputs (
tests/test_uk_replay_results_pr.py):ci.ymlchanges..gitignoreignores fails this check instead of being dropped silently.proposejob pushes only a commit it built itself, and only when its tree is the onebuildverified.Validation
ci.yml. The 6.5.0 paragraph's integrity and grid sentence matches Certify the policyengine.py 6.5.0 replay results, the first with the processed-value check #190's hand-written one word for word.78e080c6. The job-1 build (strict, the real checks) and the fresh-clone job-2 stdlib rebuild produced the same tree,803717e2. That tree differs from the hand-built Certify the policyengine.py 6.5.0 replay results, the first with the processed-value check #190 only in the note and the paragraph. All three phases took 84 s.replay-staterequestd83eaf59…against a local bare remote, with the realverify-sealed, regeneration andbuild_db, runningbuildthenproposeas the workflow does. It took 88 s. The bot's tree equals the hand-built one in all 249 changed paths except two generated texts: the record'snote(Certify the policyengine.py 6.5.0 replay results, the first with the processed-value check #190's added the Check processed replay values; revise AB2024 blended CGT for day-weighting engines #186 context) and the RELEASE_REPLAY paragraph (counts only, no CGT interpretation). Same result at 71161e9, ed76b45 and 854081d.gh:proposerefusing a commit that moved after verification, or a missing build output or token;78e080c6, the targeted replay test files pass locally with-n 4: results-PR 62, and the other six files 335 passed, 1 skipped. Atf7247f19, local runs were held for Fleet ops' disk pause, and CI found four failures, fixed in78e080c6.ruff format --checkis clean.Independent review
97c351b9: REQUEST_CHANGES. Report inw6-lanes/reviews/review_193.md. Two blocking findings, both fixed inf7247f19:ghcredential helper and the recorder dispatching nothing for a failed request;workflows: write. The brief asks for the variant to be added toci.yml's matrix, and the token no longer shares a runner with third-party code. That change is a separate follow-up.f7247f19: REQUEST_CHANGES. Report inw6-lanes/reviews/review_193_delta.md. It confirmed the two-job split closes finding 1. Two blocking findings remained, both fixed in78e080c6:gh;ci.yml;78e080c6: APPROVE (hard tier). Report inw6-lanes/reviews/review_193_delta2.md. It found no blocking issue and six non-blocking ones, which a follow-up PR handles: the tip check running before the guard, comparing records by identity, two tests that are weaker than they look, a positiveGITHUB_SHArun, and a comment nit.Not verified until it runs on GitHub
The App token path (minting, checkout with the token, the push, PR creation and the PR's
pull_requestchecks) can only run once this workflow is on main. After merge, I will dispatch it for the 6.5.0 request. That request is already committed, so the run exercises checkout, fetch, guard andcheck, then ends with a notice; the token is minted only for a built commit, so minting, the push and the PR first run on the next certified release.axiom: n/a: CI automation and tests; no policy encoding.
🤖 Generated with Claude Code