Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 11 additions & 6 deletions .claude/skills/pullrequest/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,9 @@ buy, is in [/ci](../ci/SKILL.md).)
Many sessions and their agents work the fleet at once. Four practices keep them from undoing each
other:

- **A hold on a PR is a DRAFT, and a draft needs a stated end.** `auto-arm.yml` re-arms auto-merge
on every push, so `--disable-auto` is not a hold; draft is the only state it respects. But a draft
- **A hold on a PR is a DRAFT, and a draft needs a stated end.** The control plane's PR steward
re-arms auto-merge once each new head is reviewed and answered, so `--disable-auto` is not a
hold; draft is the only state it respects. But a draft
has no expiry: write in the PR body WHAT it waits for (a named PR merged, a sealed set carrying a
commit) and re-check that condition before you leave it parked, because the blocker may already
be gone. Copilot does not review pushes to a draft, so mark it ready before the fix you want
Expand Down Expand Up @@ -412,11 +413,15 @@ that was tested β€” the fix for two independently-green PRs being red together (
changes about this procedure:

- **`gh pr merge <n> --auto` means "enqueue when this PR's own required checks are green".**
`auto-arm.yml` runs it on every non-draft PR, so a green PR lands without a hand on it. Convert to
**draft** to opt out. `gh pr merge <n> --merge` on a green PR is the same thing done by hand: it
enters the queue, it does not merge on the spot.
The control plane's PR steward arms it on a non-draft PR once its CURRENT head has its review (a
Copilot review against that head, policy `copilot-code-review`), every reviewer thread is answered
and the required checks are green (`auto-arm.yml` only DISARMS, on every push β€”
Doc/Architecture/ReviewFindingsAnswered β†’ "The arm gate"). Convert to **draft** to opt out.
`gh pr merge <n> --merge` on a green PR is the same thing done by hand: it enters the queue, it
does not merge on the spot.
- **A push to a queued branch ejects it.** The queue does not pick up the new commit in place; the
arm lane re-arms on `synchronize` and the new head re-enters once its own run is green.
push disarms it (`auto-arm.yml`), and the control plane re-arms the new head once it is reviewed,
answered and green.
- **Dequeue via GraphQL** (`dequeuePullRequest(input:{id:<PR node id>})`), never by re-ordering or
by `jump`. The queue's order is its correctness argument.
- **Never re-queue an ejected PR by hand, and never re-run the failed queue build.** The
Expand Down
48 changes: 0 additions & 48 deletions .github/lane-caller-grants.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,6 @@ repos:
job: arm
lane: auto-arm.yml
grants: {contents: write, pull-requests: write}
pending: RETIRING β€” replaced by the checks:read row below once this repository's caller grants it; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: auto-arm.yml
job: arm
lane: auto-arm.yml
grants: {checks: read, contents: write, pull-requests: write}
pending: ARRIVING β€” the caller adds checks:read (the lane reads the internal-review check run) and the check_run / pull_request_review_comment triggers; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: ci.yml
job: modules-floor
lane: node-repo-module-pack.yml
Expand Down Expand Up @@ -127,12 +121,6 @@ repos:
job: arm
lane: auto-arm.yml
grants: {contents: write, pull-requests: write}
pending: RETIRING β€” replaced by the checks:read row below once this repository's caller grants it; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: auto-arm.yml
job: arm
lane: auto-arm.yml
grants: {checks: read, contents: write, pull-requests: write}
pending: ARRIVING β€” the caller adds checks:read (the lane reads the internal-review check run) and the check_run / pull_request_review_comment triggers; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: ci.yml
job: publish-bake
lane: node-repo-publish-bake.yml
Expand Down Expand Up @@ -182,12 +170,6 @@ repos:
job: arm
lane: auto-arm.yml
grants: {contents: write, pull-requests: write}
pending: RETIRING β€” replaced by the checks:read row below once this repository's caller grants it; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: auto-arm.yml
job: arm
lane: auto-arm.yml
grants: {checks: read, contents: write, pull-requests: write}
pending: ARRIVING β€” the caller adds checks:read (the lane reads the internal-review check run) and the check_run / pull_request_review_comment triggers; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: ci.yml
job: compile-check
lane: node-repo-compile-check.yml
Expand Down Expand Up @@ -241,12 +223,6 @@ repos:
job: arm
lane: auto-arm.yml
grants: {contents: write, pull-requests: write}
pending: RETIRING β€” replaced by the checks:read row below once this repository's caller grants it; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: auto-arm.yml
job: arm
lane: auto-arm.yml
grants: {checks: read, contents: write, pull-requests: write}
pending: ARRIVING β€” the caller adds checks:read (the lane reads the internal-review check run) and the check_run / pull_request_review_comment triggers; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: ci.yml
job: compile-check
lane: node-repo-compile-check.yml
Expand Down Expand Up @@ -301,12 +277,6 @@ repos:
job: arm
lane: auto-arm.yml
grants: {contents: write, pull-requests: write}
pending: RETIRING β€” replaced by the checks:read row below once this repository's caller grants it; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: auto-arm.yml
job: arm
lane: auto-arm.yml
grants: {checks: read, contents: write, pull-requests: write}
pending: ARRIVING β€” the caller adds checks:read (the lane reads the internal-review check run) and the check_run / pull_request_review_comment triggers; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: ci.yml
job: compile-check
lane: node-repo-compile-check.yml
Expand Down Expand Up @@ -353,12 +323,6 @@ repos:
job: arm
lane: auto-arm.yml
grants: {contents: write, pull-requests: write}
pending: RETIRING β€” replaced by the checks:read row below once this repository's caller grants it; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: auto-arm.yml
job: arm
lane: auto-arm.yml
grants: {checks: read, contents: write, pull-requests: write}
pending: ARRIVING β€” the caller adds checks:read (the lane reads the internal-review check run) and the check_run / pull_request_review_comment triggers; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: ci.yml
job: compile-check
lane: node-repo-compile-check.yml
Expand Down Expand Up @@ -409,12 +373,6 @@ repos:
job: arm
lane: auto-arm.yml
grants: {contents: write, pull-requests: write}
pending: RETIRING β€” replaced by the checks:read row below once this repository's caller grants it; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: auto-arm.yml
job: arm
lane: auto-arm.yml
grants: {checks: read, contents: write, pull-requests: write}
pending: ARRIVING β€” the caller adds checks:read (the lane reads the internal-review check run) and the check_run / pull_request_review_comment triggers; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: ci.yml
job: compile-check
lane: node-repo-compile-check.yml
Expand Down Expand Up @@ -467,9 +425,3 @@ repos:
job: arm
lane: auto-arm.yml
grants: {contents: write, pull-requests: write}
pending: RETIRING β€” replaced by the checks:read row below once this repository's caller grants it; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
- workflow: auto-arm.yml
job: arm
lane: auto-arm.yml
grants: {checks: read, contents: write, pull-requests: write}
pending: ARRIVING β€” the caller adds checks:read (the lane reads the internal-review check run) and the check_run / pull_request_review_comment triggers; auto-arm arms only after the internal review (MeshWeaver PR fix/arm-after-review, 2026-10-04)
17 changes: 10 additions & 7 deletions .github/scripts/check-review-answered.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
-----
check-review-answered.py --self-test
check-review-answered.py --repo O/R --pr N [--as-of 2026-09-14T14:04:21Z]
check-review-answered.py --repo O/R --pr N --arm-gate (auto-arm.yml: may auto-merge be armed now?)
check-review-answered.py --repo O/R --pr N --arm-gate (the control plane's arm decision: may auto-merge be armed now?)
check-review-answered.py --repo O/R --merge-group-ref refs/heads/gh-readonly-queue/main/pr-N-<sha>
check-review-answered.py --repo O/R --pr N --stage-gate --since <run created_at>
(node-repo-stage-gate.yml: may the expensive suites start for this head?)
Expand Down Expand Up @@ -548,15 +548,18 @@ def reply_age_seconds(stamp: str, now: str | None = None) -> float | None:

# ─────────────────────────────── the ARM gate (pure) ───────────────────────────────
#
# `auto-arm.yml` asks a DIFFERENT, stricter question than the merge gate above: not "may this merge"
# but "may auto-merge be armed NOW". Measured 2026-10-03/04 on MeshWeaver.Plugins, where the review
# The ARM decision (made by the control plane's PR steward, MeshWeaver.Plugins `PrArming`; this is
# its reference implementation and these self-test cases its test vectors β€” `auto-arm.yml` only
# disarms on a push) asks a DIFFERENT, stricter question than the merge gate above: not "may this
# merge" but "may auto-merge be armed NOW". Measured 2026-10-03/04 on MeshWeaver.Plugins, where the review
# is comment-only and nothing required waits for it: the lane re-armed on every push and on undraft,
# BEFORE the internal review had run on the new head, so #2549, #2643, #2647 and Memex#641 merged
# with findings nobody had answered or with no review at all, and agents disarmed by hand after
# every push (#2791 twice in an hour). So the lane arms only when ALL of these hold:
# every push (#2791 twice in an hour). So auto-merge is armed only when ALL of these hold:
#
# 1. not a draft;
# 2. the internal reviewer's `internal-review` check run on the CURRENT head has COMPLETED, and it
# 2. the CURRENT head has its review β€” a Copilot review against it (see below), or the internal
# reviewer's `internal-review` check run on it has COMPLETED, and that run
# is not the neutral "Reviewer unavailable" degradation. That degradation releases the MERGE
# gate's review condition (so a down reviewer cannot hold every pull request), but it is not a
# review, and arming on it would land an unreviewed change with nobody having decided to β€”
Expand Down Expand Up @@ -1943,7 +1946,7 @@ def case(name: str, expect: tuple[str, ...], pr, reviews, comments, waiver=NO_WA
failures += 0 if ok else 1
print(f"self-test {'ok' if ok else 'FAIL':4} {name:60} expected={expect} got={got}")

# ── the ARM gate (auto-arm.yml): arms only on a completed internal review of the CURRENT head,
# ── the ARM gate (control-plane reference): arms only on a review of the CURRENT head (Copilot's, or a completed internal-review run),
# with every reviewer thread answered by a person, and never a draft. Each NO case names the
# condition it must fail ON, so a fixture cannot pass by failing for another reason.
HEAD = "a" * 40
Expand Down Expand Up @@ -2381,7 +2384,7 @@ def main(argv=None) -> int:
ap.add_argument("--pr", help="pull request number")
ap.add_argument("--merge-group-ref", help="a merge-queue head ref; the pull request number is read from it")
ap.add_argument("--arm-gate", action="store_true",
help="answer auto-arm.yml's question instead β€” may auto-merge be armed NOW (see arm_readiness); "
help="answer the arm decision's question instead β€” may auto-merge be armed NOW (see arm_readiness); "
"writes ready=true|false to $GITHUB_OUTPUT and one line to the job summary, exit 0 either way")
ap.add_argument("--as-of", help="evaluate as of this ISO-8601 UTC instant (e.g. a merged_at)")
ap.add_argument("--settle-replies", type=int, default=0, metavar="SECONDS",
Expand Down
Loading