Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
55531e9
test(ci): RED case table for the signed-receipt PR gate — signature, …
noahgift Oct 20, 2026
1f41279
feat(ci): signed-receipt verifier — signature, tree, toolchain; fail …
noahgift Oct 20, 2026
f67b72c
feat(ci): tier=reuse for a PR with a verified signed receipt — check/…
noahgift Oct 20, 2026
0b0d4a5
feat(ci): wire the receipt gate into the PR tier step; pin the batche…
noahgift Oct 20, 2026
5437955
fix(ci): receipt judge read from the base ref; signed pass verdict re…
noahgift Oct 20, 2026
aab26c4
fix(ci): receipt base fetch is inline git, not a PR-tree script (Refs…
noahgift Oct 20, 2026
81cebeb
fix(ci): receipt judge read first, in a fresh clone with replace and …
noahgift Oct 20, 2026
14e51c5
test(ci): W19 requires config-off on both the clone and the show (Ref…
noahgift Oct 20, 2026
b6a47ab
ci(receipt): the judge is its own job that never runs the PR tree (Re…
noahgift Oct 20, 2026
13664bb
ci(receipt): read the toolchain via git show, refuse path=, rustc fro…
noahgift Oct 20, 2026
aed6c45
ci(receipt): judge worktree is --no-checkout with hooks off, token en…
noahgift Oct 20, 2026
3313c5f
ci(receipt): absolute tools, env -i verifier, judge on a hosted ephem…
noahgift Oct 20, 2026
64c2971
ci(receipt): state exactly what merge_group does (quick tier, PR's ow…
noahgift Oct 20, 2026
1f02016
fix(ci): regenerate roadmap aggregate; bashrs SEC010 annotations on r…
noahgift Oct 20, 2026
b5eef8e
ci(receipt): judge on a dedicated self-hosted label, skipped until it…
noahgift Oct 20, 2026
de9e5a1
merge: origin/main (Refs #5798)
noahgift Oct 20, 2026
13f1daf
merge: origin/main (Refs #5798); check_workspace step keeps the reuse…
noahgift Oct 21, 2026
721a1f2
merge: origin/main (Refs #5798)
noahgift Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flow009/batcher.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINna9SBgvdA0Te1LhyTa6jXbDu8SgJmiDDPhHkLmljw7 flow009-batcher
56 changes: 56 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -439,10 +439,65 @@ jobs:
# The workspace tests, one shard per job (nextest hash partition N/12 plus the
# explicit runner's round-robin N/12), in parallel on the X64 clean-room pool.
# The section body is ci/sections.yml `workspace-test-shard`, unchanged.
# #5798: the receipt judge. A PR cannot forge a receipt: this job checks out the BASE only
# and fetches the PR merge commit as data (a --no-checkout worktree that is read, never
# executed), on a dedicated self-hosted ephemeral runner (label receipt-judge) that no PR-code job ever shares, and runs the
# verifier under `env -i` with absolute tools. What it does NOT cover: a PR that edits the CI
# files or the judge itself. merge_group ignores --receipt-ok but runs only the QUICK tier
# (full only on tag/release, scripts/ci_test_tier.sh merge_group case), from the PR's own
# CI files, so it does not re-judge such an edit with a trusted copy; CI-edit review is the
# only backstop. scripts/check_receipt_gate.sh (W16-W32) turns RED if
# this job gains a step that runs PR-tree code or checks the PR out as code.
# Fail closed: the job runs only once the repo variable RECEIPT_JUDGE_RUNNER_READY is 'true' (set
# after the label has a runner). Until then it is skipped, the shard gets ok=0 and the PR runs the
# tier its diff selects, so a judge with no runner never queues and never blocks a PR.
# It always exits 0; the shard job reads `ok` as 1 only on a literal 1.
receipt-judge:
if: github.event_name == 'pull_request' && vars.RECEIPT_JUDGE_RUNNER_READY == 'true'
runs-on: [self-hosted, Linux, X64, receipt-judge]
timeout-minutes: 10
permissions:
contents: read
outputs:
ok: ${{ steps.judge.outputs.ok }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.base_ref }}
persist-credentials: false
- name: Judge the receipt in the PR body
id: judge
env:
GH_TOKEN: ${{ github.token }}
PR_BODY: ${{ github.event.pull_request.body }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
export GIT_NO_REPLACE_OBJECTS=1
ok=0
printf '%s' "${PR_BODY:-}" > "$RUNNER_TEMP/pr-body.md"
export GIT_TERMINAL_PROMPT=0 GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=http.https://github.com/.extraheader
GIT_CONFIG_VALUE_0="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$GH_TOKEN" | base64 | tr -d '\n')"
export GIT_CONFIG_VALUE_0
if git fetch -q --no-tags --depth=1 origin "refs/pull/${PR_NUMBER}/merge" &&
[ "$(git rev-parse FETCH_HEAD)" = "${GITHUB_SHA}" ] &&
unset GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0 GH_TOKEN &&
git -c core.hooksPath=/dev/null worktree add -q --no-checkout --detach "$RUNNER_TEMP/prtree" FETCH_HEAD &&
git show HEAD:scripts/ci/receipt_ok_arg.sh > "$RUNNER_TEMP/rok.sh"; then
# --no-checkout: the PR tree has no files, so no PR executable can sit in the verifier's
# cwd, and no filter or hook runs. The wrapper is read from the base object database.
ok="$(cd "$RUNNER_TEMP/prtree" && env -i HOME="$HOME" PATH=/usr/local/bin:/usr/bin:/bin /usr/bin/bash "$RUNNER_TEMP/rok.sh" "$RUNNER_TEMP/pr-body.md" "$GITHUB_WORKSPACE" "${GITHUB_SHA}")" || ok=0
fi
[ "$ok" = 1 ] || ok=0
printf 'ok=%s\n' "$ok" >> "$GITHUB_OUTPUT"

workspace-test-shard:
# same lanes as x86-main: clean-room for pull_request/push, mq for a merge_group entry
runs-on: ${{ github.event_name == 'merge_group' && fromJSON('["self-hosted","Linux","X64","mq"]') || fromJSON('["self-hosted","Linux","X64","clean-room"]') }}
timeout-minutes: 180
# The judge is skipped off a pull_request and fails closed: !cancelled() runs the shard
# whatever the judge did, and RECEIPT_OK is 1 only on the judge's literal 1.
needs: [receipt-judge]
if: ${{ !cancelled() }}
strategy:
fail-fast: false
matrix:
Expand All @@ -451,6 +506,7 @@ jobs:
outputs:
results: ${{ steps.run.outputs.results }}
env:
RECEIPT_OK: ${{ needs.receipt-judge.outputs.ok || '0' }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
FAT_VARS_JSON: ${{ toJSON(vars) }}
steps:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/guards-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ jobs:
# ─────────────────────────────────────────────────────────────────────
- name: "Test-tier decision case table (BSE-17): quick/full/reuse, drift is ENV"
run: bash scripts/ci_test_tier.sh --self-test
- name: Receipt gate case table and mutants (#5797, LAB)
run: setsid --wait bash scripts/check_receipt_gate.sh
- name: Fleet-cells gate case table and mutants (#5064, LAB)
run: setsid --wait bash scripts/release/fleet_cells_gate.sh --self-test
- name: compute row zero-tests case table and mutants (#5048, LAB)
Expand Down
13 changes: 12 additions & 1 deletion ci/sections.yml
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,20 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
MG_BASE_SHA: ${{ github.event.merge_group.base_sha }}
PR_BODY: ${{ github.event.pull_request.body }}
run: |
set -euo pipefail
args=(--event "${GITHUB_EVENT_NAME}")
# #5797: a signed batch receipt in the PR body (signature, tree hash == this
# merge tree, toolchain hash) lets the PR run check/fmt/clippy instead of the
# suite. The verdict is NOT computed here: the `receipt-judge` job in ci.yml
# computes it in a job that never checks out or runs the PR tree (it checks out
# the base, reads the PR merge commit as data), and the shard job hands it in as
# RECEIPT_OK. Anything but a literal 1 -- unset, 0, a skipped or failed judge -- is
# 0, and 0 leaves the tier exactly as it was. Only a pull_request can carry one.
rok=0
if [ "${GITHUB_EVENT_NAME}" = pull_request ] && [ "${RECEIPT_OK:-0}" = 1 ]; then rok=1; fi
args+=(--receipt-ok "${rok:-0}")
# PMAT-1098 67-E2: the queue and a push to main re-derive their own
# diff from HEAD's FIRST PARENT (HEAD^1..HEAD is the PR's diff on the
# new base). actions/checkout is shallow, so that parent is not in the
Expand Down Expand Up @@ -1068,7 +1079,7 @@ jobs:
python3 scripts/ci/sigma_executed.py nextest --kind "quick tree readers" --junit "$sig/quick-tree.junit.xml" --list-json "$sig/quick-tree.list.json" || rc=1
exit "$rc"
- name: "Quick tier: cargo check --workspace (over-the-cap integration, BSE-17 rule (i))"
if: steps.tier.outputs.tier == 'quick' && steps.tier.outputs.check_workspace == '1' && matrix.shard == matrix.shards
if: ((steps.tier.outputs.tier == 'quick' && steps.tier.outputs.check_workspace == '1') || (steps.tier.outputs.tier == 'reuse' && steps.tier.outputs.cite == 'receipt')) && matrix.shard == matrix.shards
timeout-minutes: 20
env:
REASON: ${{ steps.tier.outputs.reason }}
Expand Down
18 changes: 18 additions & 0 deletions docs/roadmaps/entries/PMAT-5797.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
- id: PMAT-5797
github_issue: 5797
item_type: task
title: 'PR gate: signed batch receipt verify (sig, tree, toolchain) as tier=reuse extension'
status: planned
priority: high
assigned_to: null
created: 2026-10-20T14:55:28Z
updated: 2026-10-20T14:55:28Z
spec: null
acceptance_criteria: []
phases: []
subtasks: []
estimated_effort: null
labels:
- kind:code
notes: null
epic: 5510
18 changes: 18 additions & 0 deletions docs/roadmaps/roadmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25340,6 +25340,24 @@ Not done, pending the ruling: moving the 31 fixture files to an obviously synthe
- kind:code
notes: null
epic: 5510
- id: PMAT-5797
github_issue: 5797
item_type: task
title: 'PR gate: signed batch receipt verify (sig, tree, toolchain) as tier=reuse extension'
status: planned
priority: high
assigned_to: null
created: 2026-10-20T14:55:28Z
updated: 2026-10-20T14:55:28Z
spec: null
acceptance_criteria: []
phases: []
subtasks: []
estimated_effort: null
labels:
- kind:code
notes: null
epic: 5510
- id: PMAT-5817
github_issue: 5817
item_type: task
Expand Down
Loading
Loading