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
8 changes: 8 additions & 0 deletions .agents/skills/iterate-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,14 @@ a stacked PR targets `main` eventually and the filter matches that eventual
target, so this workflow runs on mid-stack PRs as well. Expect to see the check
on every PR in a stack and decide from stack position, not from the `on:` block.

The archive job is also skipped while a PR is a **draft**. A spec-only proposal
is its own tip until its implementation is stacked on top, so the gate would
otherwise demand it archive a change nobody has built yet, and it would sit red
for as long as the proposal is open. A draft cannot merge, and the check runs on
`ready_for_review`, so nothing unarchived can reach `main` — if a proposal PR is
red on this check, mark it ready only when its implementation is stacked
beneath it.

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:
Expand Down
25 changes: 24 additions & 1 deletion .github/workflows/pr-check-openspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,26 @@ name: PR OpenSpec Archive Check
# tip of a stack (or a standalone PR) and is skipped on PRs that still have work
# stacked on top of them. Tip = no other OPEN PR targets this PR's head branch
# as its base.
#
# Drafts are skipped. A spec-only proposal is its own tip until its
# implementation is stacked on top, so the gate would otherwise demand it
# archive a change that has not been built yet — leaving the PR red for as long
# as the proposal is open, which is how teams learn to ignore red. A draft
# cannot merge, so the guarantee that no unarchived change reaches `main` is
# unaffected: the check runs when the PR is marked ready for review.
on:
pull_request:
# Both draft-transition events are REQUIRED, and neither is in the default
# set (opened/synchronize/reopened), because the archive job's condition
# depends on draft state:
# ready_for_review — without it a draft could be marked ready and merged
# on a stale green that was never re-evaluated. This
# one protects the guarantee.
# converted_to_draft — without it a PR that failed while ready keeps that
# failure after being converted back to draft, until
# some unrelated push happens to re-run it.
types:
[opened, synchronize, reopened, ready_for_review, converted_to_draft]

permissions:
contents: read
Expand Down Expand Up @@ -53,7 +71,12 @@ jobs:
check-openspec-archived:
name: "stack: openspec-archived"
needs: stack-position
if: needs.stack-position.outputs.is_tip == 'true'
# Skipped on drafts. A proposal-only PR is its own tip — nothing is stacked
# on it yet — so the gate would demand it archive a change whose
# implementation has not been written, and it would sit red for as long as
# the proposal is open. A draft cannot merge, so nothing can reach `main`
# unarchived; the check runs the moment it is marked ready for review.
if: needs.stack-position.outputs.is_tip == 'true' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
Expand Down
Loading