From 648c2a31424a9e67dbae8ba94c3d882c23e1e82a Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Thu, 11 Jun 2026 17:32:17 -0700 Subject: [PATCH 1/2] chore(skill): Scope iterate-pr archive check to the stack tip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Teach the iterate-pr skill that the PR OpenSpec Archive Check (pr-check-openspec.yml) is expected-red on every PR in a stack except the tip. An OpenSpec change is archived exactly once, at the end of the work, so a stacked PR that still carries the in-progress change directory must not archive — doing so would remove the change docs before the implementation PRs above it merge. The skill now determines whether a PR is the tip (no open PR targets its head branch as base) and only treats the archive check as actionable there. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/skills/iterate-pr/SKILL.md | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.claude/skills/iterate-pr/SKILL.md b/.claude/skills/iterate-pr/SKILL.md index 15b17db8..b5b037a2 100644 --- a/.claude/skills/iterate-pr/SKILL.md +++ b/.claude/skills/iterate-pr/SKILL.md @@ -143,6 +143,43 @@ Run `${CLAUDE_SKILL_ROOT}/scripts/fetch_pr_checks.py` to get structured failure **Wait if pending:** If review bot checks (sentry, warden, cursor, bugbot, seer, codeql) are still running, wait before proceeding—they post actionable feedback that must be evaluated. Informational bots (codecov) are not worth waiting for. +#### Stacked PRs: ignore the OpenSpec Archive Check unless this is the last PR in the chain + +The `PR OpenSpec Archive Check` (workflow `pr-check-openspec.yml`) fails whenever +any unarchived directory exists under `openspec/changes/`. A change is archived +exactly once, at the END of the work — so in a stack of PRs that all carry the +same in-progress change directory, this check will fail on every PR. Archiving on +an intermediate PR is wrong: it would remove the change docs before the +implementation PRs above it have merged. + +So, before treating an archive-check failure as actionable, determine ONE thing: +**is this PR the last in the chain (the tip)?** The base branch is irrelevant — +the bottom PR of a stack often targets the default branch yet still has work +stacked on top of it, so it must NOT archive either. + +A PR is the tip when no other OPEN PR targets this PR's head branch as its base: + +```bash +gh pr view --json headRefName --jq '.headRefName' +gh pr list --state open --base --json number +``` + +An empty list → nothing is stacked on top → this PR is the tip. + +Then: + +- **Not the tip** (some open PR is stacked on this one) → IGNORE the + `PR OpenSpec Archive Check` failure. Do NOT archive the change on this PR. + Treat the check as expected-red and do not let it block the iterate loop + (still address every other failing check and all feedback normally). +- **The tip** (nothing stacked on top — including an ordinary standalone PR) → + the change MUST be archived before merge. Archive it (move + `openspec/changes//` under `openspec/changes/archive/` via the OpenSpec + archive flow), commit, and push so the check goes green. + +This rule applies ONLY to the OpenSpec Archive Check. Every other check is +handled normally regardless of stack position. + ### 5. Fix CI Failures For each failure in the script output: From 52ef35ab4e47a95e6e4e74a0f47bc80e10680be0 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Thu, 11 Jun 2026 18:47:35 -0700 Subject: [PATCH 2/2] chore(skill): Refine the stacked-PR archive-check guidance Address review on PR #29: - Clarify that pr-check-openspec.yml is scoped to `pull_request.branches: [main]`, so it only runs on PRs targeting main (typically the bottom of a stack and any PR retargeted to main); a PR based on another feature branch won't run or fail it at all. - Make the tip-detection commands copy/pasteable via a HEAD shell var. - Name the full dated archive destination `openspec/changes/archive/YYYY-MM-DD-/` so the change isn't moved to a non-standard location that still satisfies CI. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/skills/iterate-pr/SKILL.md | 31 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/.claude/skills/iterate-pr/SKILL.md b/.claude/skills/iterate-pr/SKILL.md index b5b037a2..fe1ad999 100644 --- a/.claude/skills/iterate-pr/SKILL.md +++ b/.claude/skills/iterate-pr/SKILL.md @@ -147,21 +147,23 @@ Run `${CLAUDE_SKILL_ROOT}/scripts/fetch_pr_checks.py` to get structured failure The `PR OpenSpec Archive Check` (workflow `pr-check-openspec.yml`) fails whenever any unarchived directory exists under `openspec/changes/`. A change is archived -exactly once, at the END of the work — so in a stack of PRs that all carry the -same in-progress change directory, this check will fail on every PR. Archiving on -an intermediate PR is wrong: it would remove the change docs before the -implementation PRs above it have merged. +exactly once, at the END of the work — so a PR that still carries an in-progress +change directory will fail this check. Archiving on an intermediate PR is wrong: +it would remove the change docs before the implementation PRs above it merge. -So, before treating an archive-check failure as actionable, determine ONE thing: -**is this PR the last in the chain (the tip)?** The base branch is irrelevant — -the bottom PR of a stack often targets the default branch yet still has work -stacked on top of it, so it must NOT archive either. +Note the workflow's trigger is currently `pull_request.branches: [main]`, so it +only RUNS on PRs whose base is `main`. A stacked PR based on another feature +branch won't run (or fail) this check at all — so there is nothing to ignore +there. The check matters for PRs that target `main`: typically the bottom of a +stack, plus any PR later retargeted to `main` as the stack merges down. -A PR is the tip when no other OPEN PR targets this PR's head branch as its base: +When the archive check does run and fail, decide ONE thing before treating it as +actionable: **is this PR the last in the chain (the tip)?** A PR is the tip when +no other OPEN PR targets its head branch as a base: ```bash -gh pr view --json headRefName --jq '.headRefName' -gh pr list --state open --base --json number +HEAD=$(gh pr view --json headRefName --jq '.headRefName') +gh pr list --state open --base "$HEAD" --json number ``` An empty list → nothing is stacked on top → this PR is the tip. @@ -173,9 +175,10 @@ Then: Treat the check as expected-red and do not let it block the iterate loop (still address every other failing check and all feedback normally). - **The tip** (nothing stacked on top — including an ordinary standalone PR) → - the change MUST be archived before merge. Archive it (move - `openspec/changes//` under `openspec/changes/archive/` via the OpenSpec - archive flow), commit, and push so the check goes green. + the change MUST be archived before merge. Archive it via the OpenSpec archive + flow, which moves `openspec/changes//` to the dated archive directory + `openspec/changes/archive/YYYY-MM-DD-/` (do not invent a different + location), then commit and push so the check goes green. This rule applies ONLY to the OpenSpec Archive Check. Every other check is handled normally regardless of stack position.