Skip to content

Commit 612e7e8

Browse files
committed
docs: name the mechanism — branches: [main] matches a stack's eventual target
`branches: [main]` reads as "only PRs whose base is main," and filtered that way when a PR had one base. Under GitHub's stacked-PR support a stacked PR targets `main` eventually, so the filter matches the eventual target and these workflows run on mid-stack PRs too — which is why #73, #80, and #81 each produced a failing `Require a changeset` run with an `openspec/partition-engine-*` base. That makes the base-ref guard required rather than defensive, and generalizes: a workflow whose correctness depends on "is this the PR that merges to main" has to establish that itself. Corrects the OpenSpec archive-check section too, which told readers a stacked PR would not run that check at all.
1 parent 514788b commit 612e7e8

3 files changed

Lines changed: 28 additions & 18 deletions

File tree

.agents/skills/iterate-pr/SKILL.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,11 @@ exactly once, at the END of the work — so a PR that still carries an in-progre
246246
change directory will fail this check. Archiving on an intermediate PR is wrong:
247247
it would remove the change docs before the implementation PRs above it merge.
248248

249-
Note the workflow's trigger is currently `pull_request.branches: [main]`, so it
250-
only RUNS on PRs whose base is `main`. A stacked PR based on another feature
251-
branch won't run (or fail) this check at all — so there is nothing to ignore
252-
there. The check matters for PRs that target `main`: typically the bottom of a
253-
stack, plus any PR later retargeted to `main` as the stack merges down.
249+
The workflow's trigger is `pull_request.branches: [main]`, but do NOT read that
250+
as "it only runs on PRs whose base is `main`." Under GitHub's stacked-PR support
251+
a stacked PR targets `main` eventually and the filter matches that eventual
252+
target, so this workflow runs on mid-stack PRs as well. Expect to see the check
253+
on every PR in a stack and decide from stack position, not from the `on:` block.
254254

255255
When the archive check does run and fail, decide ONE thing before treating it as
256256
actionable: **is this PR the last in the chain (the tip)?** A PR is the tip when
@@ -292,6 +292,14 @@ Mid-stack PRs bypass the check on their base ref. If you see one failing it,
292292
look at that guard rather than reaching for the label, which would wrongly
293293
record the change as shipping no release note.
294294

295+
**Do not read `on: pull_request: branches: [main]` as "this only runs on the
296+
bottom PR."** Under GitHub's stacked-PR support a stacked PR targets `main`
297+
eventually, and the filter matches that eventual target — so these workflows run
298+
on mid-stack PRs too. A workflow that must act only on the PR merging to `main`
299+
has to establish that from the base ref or its stack position. When judging
300+
whether a check "should even be running here," check the stack rather than the
301+
`on:` block.
302+
295303
### Never leave a PR on red
296304

297305
**No PR merges with failing tests, including mid-stack.** "Merging down" changes

.github/workflows/require-changeset.yml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,19 @@ jobs:
4444
# papered over with a `skip-changeset` label, which would wrongly
4545
# record "this change ships no release note."
4646
#
47-
# `on: pull_request` with `branches: [main]` is documented to filter on
48-
# the PR's BASE branch, which should make this unreachable. It was not:
49-
# PRs #73, #80, and #81 — bases `openspec/partition-engine-*`, never
50-
# `main` — each produced a failing `Require a changeset` check run
51-
# (e.g. run 30786954198, event `pull_request`, head
52-
# `openspec/partition-engine-2-dispatch`, associated PR #80 with a
53-
# feature-branch base). A workflow that never triggers produces no
54-
# check run at all, so those runs are the evidence.
47+
# This guard is required, not belt-and-braces. `branches: [main]` reads
48+
# like "only PRs whose base is main," and that is how it filtered when
49+
# a PR had exactly one base. Under GitHub's stacked-PR support a PR in
50+
# a stack is understood to target `main` *eventually*, so the filter
51+
# matches on the eventual target and the workflow runs on mid-stack
52+
# PRs too. Observed here: #73, #80, and #81 — bases
53+
# `openspec/partition-engine-*`, never `main` — each produced a failing
54+
# `Require a changeset` check run (e.g. run 30786954198, event
55+
# `pull_request`, head `openspec/partition-engine-2-dispatch`).
5556
#
56-
# Treat this as a defensive guard: if the trigger filter behaves as
57-
# documented it costs one string comparison, and where it does not, it
58-
# is what keeps a mid-stack PR from being told to add a second
59-
# changeset.
57+
# So `branches:` no longer scopes a workflow to the bottom of a stack.
58+
# Any job whose correctness depends on "is this the PR that merges to
59+
# main" has to establish that itself, as this one does.
6060
if [ "$BASE_REF" != "main" ]; then
6161
echo "Base is '$BASE_REF', not 'main' — mid-stack PR, so the bottom PR of the stack carries the changeset."
6262
exit 0

CLAUDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ Note how this interacts with the archive gate (see the OpenSpec archive check be
9393
- **The changeset belongs on the bottom PR**, the one that targets `main`. That is the only place it can live: a changeset added on the tip is invisible to the bottom PR's diff, so the check would fail on the PR that actually merges.
9494
- **Mid-stack PRs bypass the check**, because the base branch is not `main`. They inherit the base's changeset rather than adding one, so there is nothing for the check to find. **Do not label them `skip-changeset`** — the label records a deliberate "this change ships no release note," which is false here, and the bypass already handles it.
9595

96-
The bypass is an in-step check on the base ref. `on: pull_request` with `branches: [main]` is documented to filter on the base branch, which should make that redundant — but it did not hold here: #73, #80, and #81 all produced failing `Require a changeset` check runs with `openspec/partition-engine-*` bases, and a workflow that never triggers produces no check run at all. Keep the guard, and if you see a mid-stack PR failing this check, look there rather than reaching for the label.
96+
The bypass is an in-step check on the base ref, and it is load-bearing. **`branches: [main]` no longer means "only the PR whose base is `main`."** Under GitHub's stacked-PR support, a PR in a stack is understood to target `main` eventually, so the filter matches on that eventual target and the workflow runs on mid-stack PRs as well — observed here on #73, #80, and #81, all with `openspec/partition-engine-*` bases.
97+
98+
The general rule that follows: **any workflow whose correctness depends on "is this the PR that merges to `main`" must determine that itself** — from the base ref, or by resolving stack position — and cannot lean on the `on:` filter to scope it. If you see a mid-stack PR failing this check, look at that guard rather than reaching for the label.
9799

98100
Put the changeset at the base and every branch above inherits it, since a child contains its ancestors' commits.
99101

0 commit comments

Comments
 (0)