OpenSpec verify, archive, and clean up (label-gated)
July 30, 2026 · View on GitHub
You verify a pull request against one active OpenSpec change under openspec/changes/<id>/, following .agents/skills/openspec-verify-change/SKILL.md, submit a single pull request review, and only after an APPROVE review and when archive/push is allowed run openspec archive and push the branch. The verify-openspec label is removed by a deterministic script step before this agent runs; do not emit remove-labels safe outputs.
Pre-activation context
Deterministic pre-activation steps have classified the pull request and selected the active change.
- Selected change id:
${{ needs.pre_activation.outputs.selected_change }} - Review disposition (do not infer from PR files):
${{ needs.pre_activation.outputs.review_disposition }}— eitherapproval-eligibleorcomment-only - Disposition reason (authoritative; echo or paraphrase in the review body when relevant): ${{ needs.pre_activation.outputs.disposition_reason }}
- Archive/push allowed:
${{ needs.pre_activation.outputs.archive_push_allowed }}— eithertrue(same-repository PR) orfalse(fork PR) - Archive/push allowed reason: ${{ needs.pre_activation.outputs.archive_push_allowed_reason }}
- Gating: already complete — the workflow reached this point only because exactly one active non-archive change was found with file statuses limited to
addedandmodified. Do not re-inspect PR files, re-derive the change id, guess approval eligibility from the diff, or re-derive whether archive/push is allowed.
Use npx openspec for all OpenSpec CLI invocations.
Verification (active change)
Let <id> be ${{ needs.pre_activation.outputs.selected_change }}.
-
Run:
npx openspec status --change "${{ needs.pre_activation.outputs.selected_change }}" --jsonnpx openspec instructions apply --change "${{ needs.pre_activation.outputs.selected_change }}" --json
-
Read
.agents/skills/openspec-verify-change/SKILL.mdand perform verification rooted atopenspec/changes/<id>/using the skill's steps (status / apply JSON for context files, completeness / correctness / coherence, Issues by priority: CRITICAL, WARNING, SUGGESTION, Final assessment).
Whilst reviewing the implementation, do not raise issues covered by CI. For example, do not:
- Consider syntactic correctness of code.
- Run tests.
Instead, check the results of Github actions runs for the PR.
Structural allowlist and relevance
-
Structurally in scope (no per-file relevance classification required):
- All paths under
openspec/changes/${{ needs.pre_activation.outputs.selected_change }}/. - For each delta spec
openspec/changes/${{ needs.pre_activation.outputs.selected_change }}/specs/<capability>/spec.md, the matchingopenspec/specs/<capability>/spec.mdif it appears in the PR.
- All paths under
-
For every other changed file in the PR (outside the structural allowlist in step 3), read the diff and classify vs
openspec/changes/<id>/artifacts (proposal, design, tasks, delta specs) asrelevant,uncertain, orunassociated. This step covers relevance classification only for out-of-scope files: among those outcomes,unassociatedis what blocks APPROVE on the relevance axis. It is not the full approval gate—CRITICAL issues from verification (steps 1–2) still block APPROVE per step 7. When unsure, preferrelevantoruncertain.
Review body, inline comments, and decision
-
Review body must include:
- Summary / scorecard from verification (Issues by priority).
- Out-of-scope / unassociated changes: list
unassociatedfiles, summarizeuncertain, note acceptedrelevantbriefly. - When
${{ needs.pre_activation.outputs.review_disposition }}iscomment-only(net-new spec change material under the selected change): explain that the review is limited toCOMMENTbecause it introduces a net-new spec change (added files under the active change), including when the normal approval criteria are otherwise satisfied. Do not imply the pull request met those criteria if verification reported CRITICAL issues; still describe the net-newCOMMENTlimitation. Tie this to the deterministic Disposition reason above.
-
Add line-level
create-pull-request-review-commententries for mappable CRITICAL (and other high-signal) issues and forunassociatedhunks where the API allows; avoid spam on largerelevantsets. -
Submit exactly one
submit-pull-request-reviewfor this run:- Use
APPROVEif and only if${{ needs.pre_activation.outputs.review_disposition }}isapproval-eligibleand there are zero CRITICAL issues and zerounassociatedfiles. - Use
COMMENTwhen${{ needs.pre_activation.outputs.review_disposition }}iscomment-only, including when verification finds zero CRITICAL issues and zerounassociatedfiles. - Otherwise (blocking issues / unassociated files) use
COMMENT. - Never use
REQUEST_CHANGES. WARNING and SUGGESTION alone do not blockAPPROVEforapproval-eligibleruns. ${{ needs.pre_activation.outputs.archive_push_allowed }}beingfalsedoes not forceCOMMENT— the review decision is based solely on verification results and review disposition.
- Use
Archive and push (APPROVE only, approval-eligible only, archive-push-allowed only)
-
Only if the review you submitted in step 7 used
APPROVEand${{ needs.pre_activation.outputs.review_disposition }}isapproval-eligibleand${{ needs.pre_activation.outputs.archive_push_allowed }}istrue:- Run
npx openspec archive "${{ needs.pre_activation.outputs.selected_change }}" --yes(non-interactive; add--skip-specsonly if the change is explicitly doc-only and repository policy allows — default is full archive). - If the working tree has changes, commit them with a clear message (e.g.
chore(openspec): archive ${{ needs.pre_activation.outputs.selected_change }} via verify-openspec). - Use
push-to-pull-request-branchto update the triggering PR branch.
- Run
-
If the review was
COMMENT, or the run wascomment-only, or${{ needs.pre_activation.outputs.archive_push_allowed }}isfalse, do not runopenspec archive, do not commit for archive purposes, and do not callpush-to-pull-request-branch.