Skip to content

Automate the results PR for a certified UK replay qualification - #193

Merged
MaxGhenis merged 9 commits into
mainfrom
w6/results-pr-bot
Oct 18, 2026
Merged

MaxGhenis merged 9 commits into
mainfrom
w6/results-pr-bot

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

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

  • Trigger. When the record job (.github/scripts/uk_replay_record.sh) writes terminal status complete, it dispatches .github/workflows/uk-replay-results.yml with the request_id. If that dispatch fails, the job warns and the queue continues. The workflow can also be run by hand. Its only trigger is workflow_dispatch, so it never runs on pull_request. Its single job runs only on the default branch's code, and each request gets its own concurrency group.
  • Builder. .github/scripts/uk_replay_results_pr.sh and pipeline/uk_replay_results_pr.py (check, apply, scope, body) take these steps in order:
    1. Read requests/<rid> from replay-state and pass it through the receiver's output guard again.

    2. Check eligibility. The request needs STATUS complete, a receipt that validates and matches STATUS, a REQUEST.json that hashes to the id, and a results/VARIANT.json that is certified, is the receipt's variant and was built from this request. Failed, rejected and candidate runs, and variants already committed, end with a notice.

    3. Refuse anything inconsistent:

      • a results tree holding anything beyond the namespace, its release diff and its freeze;
      • a namespace receipt that differs from the request's receipt, or a manifest or diff the receipt doesn't bind;
      • any path that main already holds;
      • results changed after the receipt commit;
      • a policyengine version older than the newest certified one (appended, it would become latest_certified);
      • a variant id that couldn't name a branch and paths.

      An exact re-qualification of a committed pair (W4's determinism check returns the committed record, note included) is skipped, not refused.

    4. Extract requests/<rid>/results/{results,docs} with git archive | tar -x --strip-components=3.

    5. Append VARIANT.json plus a note to the index with compute_uk_event.canonical_bytes.

    6. Add the matrix entry. After the edit, the matrix must list exactly the certified variants, and nothing else in ci.yml may change.

    7. Move NEWEST in the qualify and determinism tests and extend COMMITTED in the variants test.

    8. Add a RELEASE_REPLAY.md paragraph built only from receipt counts: integrity, grids and diff flags.

    9. Commit, and refuse the commit if it touches any other path.

    10. Check that every committed result file has the blob replay-state holds.

    11. Run verify-sealed (the new namespace, then all of them) and CI's uk_replay_regenerate.sh, then build the database twice with scorecard_db.build_db: the content hashes must match and no tracked file may drift.

    12. 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 not complete.

  • Authority, in two jobs on two runners.
    • build runs steps 1–11. Its GITHUB_TOKEN is contents: read, the checkout persists no credential, and it holds no secret. It outputs the verified tree, the main and replay-state commits it used, and two check results.
    • propose runs on a fresh runner, with no uv or installed package and its actions pinned to commits. It assembles the same commit again with git and python3 -I -S (stdlib only) on the commits build used, which must be in main's and replay-state's history. It refuses unless its tree equals build's. Only then does it mint a token for the organisation's policyengine GitHub App, with actions/create-github-app-token (v2.2.2, pinned) and the org secrets APP_ID and APP_PRIVATE_KEY. The token is scoped to this repository with contents, pull-requests and workflows write, the same App policyengine-us's weekly lock PR and policyengine-api's bundle update use.
    • Two jobs, not two steps: steps in one job share a runner, so anything the checks' PyPI packages did there (a $GITHUB_PATH shim, say) would reach the step holding the token.
    • Neither job holds a dataset token, and neither installs, imports or runs candidate or release code.

Why an App token, not GITHUB_TOKEN

The first commit here used GITHUB_TOKEN and dispatched CI on the branch. That design could not work, for two reasons:

  • Workflow files. The commit edits .github/workflows/ci.yml. A push that changes a workflow file needs workflows permission, which GITHUB_TOKEN can never hold.
  • Checks. A PR opened with GITHUB_TOKEN starts no pull_request run (docs). Checks from a workflow_dispatch run don't count for a PR (docs), so gh pr checks would never have passed.

gh api /orgs/PolicyEngine/installations shows the policyengine App installed on all repositories with contents, pull_requests and workflows write. Its push and PR start ordinary pull_request CI.

Tests made editable, discovery made stable

  • tests/test_uk_replay_variants.py: the committed list is now one COMMITTED tuple the builder appends to.
  • tests/test_uk_replay_determinism.py: POST_EVIDENCE pins the first namespace sealed after Check processed replay values; revise AB2024 blended CGT for day-weighting engines #186, so NEWEST only drives the base_namespace check.
  • 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):

  • Append-only index. The new index is the old one plus one record, in canonical form.
  • Matrix. After the edit, the matrix lists exactly the certified variants; nothing else in ci.yml changes.
  • Paragraph. It states every nonzero flag count and no zero one.
  • Scope. A commit touches the extracted results and the six edited files, nothing else.
  • Bytes. Every committed result file has replay-state's blob. A sealed file under a path main's .gitignore ignores fails this check instead of being dropped silently.
  • Idempotence. The same request on the same main pushes nothing the second time. A rebuild force-pushes, with a lease, only over exactly one commit that the bot both authored and committed, with this request's trailer, so it never overwrites a person's commit, amended ones included.
  • Same tree in both jobs. The propose job pushes only a commit it built itself, and only when its tree is the one build verified.
  • Order. Every guard runs before the commit or push it protects.

Validation

Independent review

  • Hard-tier review of 97c351b9: REQUEST_CHANGES. Report in w6-lanes/reviews/review_193.md. Two blocking findings, both fixed in f7247f19:
    1. The build/propose split was two steps in one job, so the build's PyPI packages could reach the token through the shared runner. Now there are two jobs, and the second rebuilds with the stdlib only.
    2. An amended bot commit kept its author and trailer, so a rerun could overwrite a reviewer's edit. Now the committer must be the bot too, and the branch must hold exactly one commit.
  • Non-blocking items fixed:
    • an id main holds for another identity is refused instead of skipped;
    • the actions in the token job are pinned to commits;
    • new tests for the force-with-lease race, the gh credential helper and the recorder dispatching nothing for a failed request;
    • grammar in the PR-body table;
    • doc wording.
  • Kept as is: reading the CI matrix from the index, which would drop workflows: write. The brief asks for the variant to be added to ci.yml's matrix, and the token no longer shares a runner with third-party code. That change is a separate follow-up.
  • Delta review of f7247f19: REQUEST_CHANGES. Report in w6-lanes/reviews/review_193_delta.md. It confirmed the two-job split closes finding 1. Two blocking findings remained, both fixed in 78e080c6:
    1. CI had four failures. Three were test bugs: a prose match, a probe the lazy imports made vacuous, and a stub dir colliding with the fixture.
    2. The fourth was a regression: rerunning a merged request failed instead of skipping, because main's record carries the note. Records are now compared without it.
  • Non-blocking items from that review, also fixed:
    • job 2 refuses a base older than the workflow's commit, and decides again on main's tip, so a rerun after the merge skips;
    • the credential test also runs the real gh;
    • a test checks the stdlib and strict matrix edits are byte-identical on the real ci.yml;
    • doc wording.
  • Second delta review of 78e080c6: APPROVE (hard tier). Report in w6-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 positive GITHUB_SHA run, 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_request checks) 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 and check, 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

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>
@vercel

vercel Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
policyengine-scorecard Ready Ready Preview Oct 18, 2026 6:13pm UTC

Request Review

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
MaxGhenis marked this pull request as ready for review October 18, 2026 19:42
@MaxGhenis
MaxGhenis merged commit 2c89c52 into main Oct 18, 2026
11 checks passed
@MaxGhenis
MaxGhenis deleted the w6/results-pr-bot branch October 18, 2026 19:45
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>
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>
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

1 active deployment
Preview — 78e080c6 Deployed Oct 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant