openspec-pr
Open, attach, or refresh optional GitHub PR delivery metadata/evidence for one OpenSpec story. Does not change story Status.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Open, attach, or refresh optional GitHub PR delivery metadata/evidence for one OpenSpec story. Does not change story Status.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Archive a locally completed OpenSpec change workspace after pre-flight checks (review approved, tasks done, PR merged or explicitly waived). Thin wrapper over /opsx:archive.
Absorb structured review/tool, PR, or reviewer feedback into an OpenSpec initiative by routing it to story edits, review rework, story candidates, or initiative-level decisions. Use when feedback needs to be incorporated without bloating or drifting stories.
Interview-driven OpenSpec initiative planning — creates openspec/initiatives/<slug>/initiative.md with goal, context, story candidates, decisions, constraints, and external resources. Use when starting a new body of work that needs an OpenSpec-backed initiative before any change workspaces can be drafted.
Inspect the current or selected OpenSpec initiative, change, or spec state and recommend the single next workflow action with concise reasoning. Use when you need lightweight lifecycle routing before choosing a planning, implementation, PR, feedback, or archive command.
Claim one ready, unclaimed story from an OpenSpec initiative and execute it end-to-end, leaving a clean handoff. Use when starting a fresh session on a new story in an OpenSpec change workspace.
Run fresh claim/resume implementation passes against one OpenSpec change until it reaches Status: 🟣 IN REVIEW, becomes blocked, or the loop reaches a hard stop. Use when a plan-approved implementation story needs continuation only; independent review is intentionally left to a separate fresh session.
| name | openspec-pr |
| description | Open, attach, or refresh optional GitHub PR delivery metadata/evidence for one OpenSpec story. Does not change story Status. |
| disable-model-invocation | true |
| argument-hint | <initiative-slug> <story-slug> [pr-url|OPEN=true] |
| allowed-tools | Read Edit Write Grep Glob Bash(git status:*) Bash(git log:*) Bash(git branch:*) Bash(git rev-parse:*) Bash(git worktree:*) Bash(git diff:*) Bash(git ls-files:*) Bash(git hash-object:*) Bash(sha256sum:*) Bash(shasum:*) Bash(gh pr list:*) Bash(gh pr view:*) Bash(gh pr edit:*) Bash(gh pr create:*) Bash(curl:*) |
Open, attach, or refresh a GitHub PR for a locally completed OpenSpec story and record delivery metadata on the change workspace's progress.md. This is a lightweight delivery helper after local review has already marked the story ✅ DONE; it is not a story lifecycle state and never updates story.md → Status:.
Argument: $ARGUMENTS — <initiative_slug> <story_slug> [<pr_url_or_OPEN=true>]. Initiative and story may be inferred only when exactly one non-archived, locally DONE story is eligible. The third arg is either a full GitHub PR URL (attach mode) or the literal OPEN=true to have this flow open the PR via gh (open mode). Omitting the third arg means refresh an existing ## PR State URL when present, otherwise discover an existing branch PR, otherwise ask before opening a new PR.
This flow applies after /openspec-story-review has approved local product/spec correctness and written Status: ✅ DONE. GitHub PRs are external delivery/review channels: they may be useful or required before archive, but they are not the authority for local story completion.
Use this command to:
gh, attach an existing PR URL, or refresh metadata for an already-bound PR;progress.md → ## PR State as durable PR delivery evidence for archive preflight.If a PR reviewer requests changes, do not downgrade the story here. Route actionable PR feedback through /openspec-feedback, which can reopen the story for resume, amend planning/contract artifacts, create a follow-up candidate, record an initiative decision, or defer/reject the feedback.
<workspace_root> = <cwd> and remains the launch checkout/worktree-discovery base.<openspec_root> = the transient active coordination artifact anchor resolved in Phase 0; never persist an OpenSpec root: field.<initiative_dir> = <openspec_root>/openspec/initiatives/<initiative>.<initiative_file> = <initiative_dir>/initiative.md.<change_dir> = <openspec_root>/openspec/changes/<story-slug>.<story_file> = <change_dir>/story.md.<progress_file> = <change_dir>/progress.md.<proposal_file> = <change_dir>/proposal.md.<review_evidence> = story.md Status header and, optionally, notebook openspec-review-<initiative_slug>-<story_slug>.There is no MASTER.md, no tracker table, and no PR lifecycle status. All status is self-contained in the change workspace artifacts:
Status: header field in <story_file> is the authoritative implementation status and is not changed by this command.Plan: header field in <story_file> is the authoritative planning lane.story.md → Initiative: is the authoritative initiative association. Inventory the top-level header region before the first ## heading for every unindented Initiative or Initiative-like field line. Exactly one present line is valid only when its whole line matches ^Initiative: ([a-z0-9]+(?:-[a-z0-9]+)*)$; duplicate, empty, whitespace-before-colon, non-canonical, or otherwise malformed Initiative-like lines are hard conflicts, never legacy absence.Status: ✅ DONE in <story_file> is local completion authority. A modern bound story requires exactly one complete canonical progress.md → ## Implementation Review Receipt; receipt absence is compatible only for a true pre-v3 story with zero Initiative or Initiative-like lines and zero receipt sections.## Current Claim section in <progress_file> records implementation state and worktree bindings.## PR State section in <progress_file> is the sole PR metadata/evidence location.## Progress Timeline section in <progress_file> records milestone bullets.⬜ TODO — not started🔄 IN PROGRESS — actively being worked🟣 IN REVIEW — ready for independent local review✅ DONE — local workflow completed by independent /openspec-story-review approval⛔ BLOCKED — explicit blockerThis flow accepts three positional inputs in $ARGUMENTS — <initiative>, <story>, and <pr_url_or_OPEN=true> — and runs three independent inference passes for any of them that is missing. Explicit values always win and skip their corresponding inference pass. The goal is convenience without silently selecting completed history from a crowded workspace.
Parse $ARGUMENTS first. Treat any of the three slots that is empty as a request to infer. Validate every explicit or inferred initiative/story slug against ^[a-z0-9]+(?:-[a-z0-9]+)*$ before path construction. Record <explicit_pair> as true only when the operator supplied both initiative and story positional arguments in this invocation; an inferred/defaulted selector or initiative alone does not make an explicit pair. This command accepts no WORKTREE= selector.
<workspace_root>=<cwd> and initially set <openspec_root>=<workspace_root>. <openspec_root> is transient runtime state only.<workspace_root> plus git worktree list --porcelain. For a known initiative/story pair, a root qualifies only when it contains both <root>/openspec/initiatives/<initiative>/initiative.md and <root>/openspec/changes/<story>/story.md. For missing selectors, collect bounded eligible pairs from those same artifact families and validate binding as steps 3–4 require; do not associate a story merely because an initiative file contains similar prose.## heading for every unindented Initiative or Initiative-like field line. Exactly one present line is valid only when its whole line matches ^Initiative: ([a-z0-9]+(?:-[a-z0-9]+)*)$. Duplicate canonical headers, an empty value, whitespace before the colon (for example Initiative : foo), a non-canonical value, or any other malformed Initiative-like line halts and reports every offending line; never treat malformed present input as header absence. For the selected story, an explicit/inferred initiative conflict is an Initiative mismatch: halt, report both values and candidate root, and do not proceed. During broad initiative/story discovery, filter a well-formed story bound to another initiative as unrelated instead of halting the PR scan; never re-associate it from prose.## Story Candidates. Exactly one association may drive discovery and must equal any selected initiative; a different or multiple association halts. No association is accepted only for a selected story when <explicit_pair> is true and the selected same-root initiative file exists. An inferred/defaulted or initiative-only selector is not an explicit pair. Warn for every accepted legacy story and never backfill the header. During broad discovery and story menus, include bound stories plus uniquely associated zero-line legacy stories only; exclude zero-association legacy stories.WORKTREE= selector, inspect registered worktrees other than <workspace_root> on refs/heads/<initiative>/<story> first. Exactly one qualifying branch worktree outranks launch even when launch has matching stale artifacts; multiple qualifying branch worktrees halt for operator selection. Only when no branch worktree qualifies, fall back to <workspace_root> and require both artifacts there. Ignore unrelated/non-branch matching roots rather than selecting one arbitrarily. Set <openspec_root> and recompute every coordination path.openspec/... reads and writes use <openspec_root>. If the launch fallback lacks the active pair, ask the operator to rerun from the checkout containing both artifacts; do not silently select a non-branch copy.<initiative> is empty)initiative.md, deduplicating each discovered story pair with the branch-over-launch precedence above.initiative.md exists and at least one non-archived story selected by that precedence is authoritatively bound to it by story.md → Initiative: and has Status: ✅ DONE. A legacy story is discoverable only through exactly one exact same-root candidate association; zero-reference legacy stories require an operator-explicit pair and therefore never appear in this inference pass.inferred initiative: <slug> (single eligible initiative).<initiative>/<story> | <Status> | <title> and emit its state-correct route from the DONE-only qualification rules below. If multiple exist, list all and use the singular Operator decision: select a lifecycle target by stable <initiative>/<story> identifier; none is eligible PR context. If none exist, require initiative/story correction. Abort before PR inference in every case.multiple eligible initiatives; pass <initiative> explicitly to disambiguate.<story> is empty)After the initiative is known, list active change workspaces across candidate roots, deduplicating each slug with branch-over-launch precedence. For each, read its authoritative story.md → Initiative: binding first. Include only stories bound to the selected initiative or legacy stories with exactly one same-root candidate association to it; filter well-formed stories bound to other initiatives as unrelated rather than halting. Exclude zero-reference legacy stories because an inferred story is not an explicit pair. A conflict on an explicitly selected story still halts. Then read Status: and collect every matching story whose status is ✅ DONE.
inferred story: <story-slug> — <title> (status: ✅ DONE).<story-slug> | <Status> | <title> and abort with the singular Operator decision: select a lifecycle target by stable story slug; no candidate is eligible for PR delivery. Do not select one merely because only one candidate is IN REVIEW or IN PROGRESS.diagnostic only: <story-slug> | <Status> | <title>, apply the state-correct diagnostic routing below, and abort before PR inference. Never call it inferred or resolved PR context.<story-slug> | ✅ DONE | <title> and abort with: multiple locally DONE stories are eligible; pass <story> explicitly to disambiguate.Before Pass 3, read the explicitly selected or DONE-inferred workspace and qualify it. A story becomes resolved PR context only after all of these checks pass: it is active/non-archived, its authoritative story.md → Initiative: binding matches, it has no blocked.md, it has authoritative Status: ✅ DONE, it has unambiguous Plan: 🟢 PLAN APPROVED, and bounded task/implementation approval evidence does not contradict DONE.
Consume progress.md → ## Implementation Review Receipt as follows:
## Implementation Review Receipt section and exactly one occurrence of every canonical field: Reviewed at, Decision, Approval gate, Status transition, Evidence reviewed, Identity method, Identity digest, Identity bases, Identity paths, Findings, Proof, and Next owner. Require Decision: APPROVE, Approval gate: PASS, a Status transition ending in ✅ DONE, Identity method: review-identity-v1, one canonical sha256:<lowercase-hex> digest, and reproducible canonical JSON bases/path arrays ([] is valid for an empty list). Duplicate headings/bodies/fields, omitted or extra entry-shaped fields, REQUEST CHANGES, BLOCKED, FAIL, malformed values, or contradictory content blocks PR operations and routes only to Open a completely fresh, oblivious session and run /openspec-story-review <initiative> <story-slug>. Never search for an older approval; fresh substantive review owns normalization to one current receipt.gh mutation or progress.md write, recompute the story-scoped identity with canonical review-identity-v1 using exactly the receipt-recorded Identity bases and Identity paths. Require an exact match to the receipt's Identity digest and resolve every recorded base/path without substitution. Missing paths, an unavailable base, unsupported/malformed identity input, or mismatch routes only to the same fresh oblivious review; this command never repairs or synthesizes receipt evidence. Save the matching digest and the last pre-mutation UTC verification timestamp in memory for PR State write-back.review-identity-v1 excludes the story's OpenSpec coordination artifacts. Therefore this command's own ## PR State and ## Progress Timeline writes do not change the identity and must not trigger a post-write recomputation; source/path drift remains a mismatch at the required pre-mutation recomputation.Status: owns routing. A receipt left from an earlier completed review may be historical context but never overrides the current non-DONE lane or makes the story eligible for PR delivery.An explicit or diagnostic non-DONE candidate is never resolved PR context and never reaches PR inference. Route it without mutation in this order:
blocked.md -> one scalar operator action to resolve the blocker and remove the file.Status: 🟣 IN REVIEW -> one fresh, oblivious /openspec-story-review <initiative> <story-slug> route even when Plan contradicts it; the wrapper never launches review.For a DONE candidate, any non-approved, missing, malformed, or ambiguous Plan is a contradictory durable state. Abort with only Operator action: investigate and reconcile the contradictory durable Status: ✅ DONE and Plan: <value> state before PR delivery or archive. Do not recommend planning commands that reject DONE and do not invent a lifecycle owner. If Plan is approved but bounded tasks or implementation approval evidence contradicts DONE, route only to a completely fresh, oblivious story-review session. Only a DONE candidate passing these checks is <resolved_story>.
<pr_url_or_OPEN=true> is empty)After the DONE story is resolved, decide whether this is an attach (existing PR) or open (new PR) operation. Walk the inference chain in order:
PR State section. Read <progress_file> and look for a ## PR State section. If it exists and has a - PR URL: line with a non-empty URL, that is the existing PR. Use attach mode in refresh form. Print: inferred PR (from PR State): <url>. Skip the rest of the chain.
Project repo detection. Read <progress_file> for the ## Current Claim section and parse the - Primary write surfaces: field. Take the first path. Walk up the directory tree until you find a .git/ directory; that is the project repo. If no .git/ is found, fall back to the workspace .git/ if one exists. If still none, skip directly to step 4.
Branch-based PR lookup.
git rev-parse --abbrev-ref HEAD to get the current branch.Operator action: check out or create and push the story feature branch, then rerun /openspec-pr <initiative> <story-slug> OPEN=true. Operating directly on main/master is not how PRs are opened; do not also offer another route.gh pr list --head <branch> --state all --json url,number,headRefName,title,state,isDraft,mergedAt,mergeCommit,closedAt,reviewDecision,latestReviews from inside the project repo. Use all states so this helper can discover already merged or closed PRs instead of opening duplicates.inferred PR (from current branch <branch>): <url> (state: <state>) and ask the user to confirm before attaching. The branch may legitimately host work unrelated to this story.
<number> | <state> | <title> | <url> and ask which to attach. Highlight merged and closed-unmerged candidates; do not fall through to implicit OPEN mode until the operator explicitly rejects/ignores the existing candidates.Fall through to OPEN mode. If no existing PR was found by any previous step, or the operator explicitly chose replacement OPEN mode after reviewing branch PR candidates:
no existing PR found for the detected story branch/repo. Open a new one via gh? [Y/n]OPEN=true path in "PR creation mode".pass <pr_url> explicitly when one exists, or rerun with OPEN=true to open a fresh PR.Before doing any work that affects progress.md or the PR, print a single resolved-context block so the user can verify what was inferred:
Resolved context:
- initiative: <slug> (explicit | inferred from single eligible initiative)
- story: <story-slug> — <title> (explicit | inferred from single eligible story)
- status: ✅ DONE
- PR: <url> (explicit | from PR State | from current branch | new via OPEN)
Print this even when everything was passed explicitly — the printout is the contract the user approves before any PR/body update runs.
Immediately before any PR/body/progress mutation, re-resolve <openspec_root>, re-read and inventory the already resolved story's complete top-level Initiative-like header region by the rules above, and repeat the full DONE-only qualification. Legacy receipt absence is allowed only for the exact zero-Initiative-like/zero-receipt pre-v3 case. A present receipt must be the one complete canonical APPROVE/PASS record with a transition ending in DONE, and canonical review-identity-v1 must recompute from its recorded bases/path list to exactly its recorded digest. If the root becomes ambiguous, the story is archived, blocked.md appeared, Status is no longer DONE, Plan is no longer unambiguously approved, receipt evidence is malformed/missing for a bound story, or identity evidence mismatches or cannot be verified, abort without any gh or progress action using the same fresh-review route above. Save the matching digest and current UTC timestamp only after this final pre-mutation recheck. Never print a resolved-context block whose status is not ✅ DONE.
Do not normalize or mutate story.md → Status: from this command.
Primary write surfaces in progress.md → ## Current Claim. If a story's surfaces span multiple repos, pass the PR URL explicitly.## Current Claim section in progress.md cannot have their project repo inferred. This usually means the story has never been claimed via /openspec-story-claim / /openspec-story-resume. Pass the PR URL explicitly in that case.This is the most important rule of this flow. The PR body is a spec-vs-code verification contract for the GitHub reviewer, not a developer diary. The reviewer must be able to answer one question from the PR body alone: does this code deliver what the story promised?
Extract only product-facing content from the change workspace artifacts (proposal.md, story.md, and delta specs under specs/):
Covers: A<n> and help the reviewer understand expected behaviorTriggering Need, Purpose, Scope, Goal / Context, External Resources, or other product-facing prose, when presentspecs/ delta specs, when they clarify the user-visible behavioral contract and fit the inclusion boundarySuppress anything that describes how the code was implemented rather than what it delivers:
## Current Claim (session-local metadata)## Progress Timeline (implementation diary with timestamps)## Session Handoff (inter-session handoff notes)Status: header in story.md (prior review approval signal)design.md content (architecture decisions, rationale, internal module structure)tasks.md content (task checklists, implementation ordering)progress.md content (runtime tracking)## Discovery Notes, ## Locked Decisions, ## Implementation Notes, ## Critical Files from story.md (implementation-facing sections)Test the inclusion boundary: source-supported catalyst context, user-visible before/after state, and external compatibility facts belong when they remain true regardless of implementation. Other content does not belong if it would become stale under a completely different implementation that still passes the Acceptance criteria and preserves the Contract changes.
Generate the body using this structure. Omit any section that has no content rather than writing "N/A".
## Summary
<one or two short paragraphs in product language. Start with the source-supported catalyst: what happened or changed, why the work is needed now, and any explicit cause-versus-exposure distinction. Then state the user-visible before/after outcome this PR delivers. If the artifacts state no catalyst, lead with the strongest source-supported Goal, Purpose, or outcome without inventing a gap, history, or causality.>
## Original tickets
- <optional label>: <url>
## Requirements
<bulleted list extracted from the story's Purpose / Goal>
## Acceptance criteria
<bulleted list extracted from the story's Acceptance section, rephrased in reviewer-verifiable terms>
## Contract changes
<only if the story changes external contracts: config keys added/removed/renamed, CLI flag changes, API/file-format changes, migration requirements, user-visible defaults>
## Out of scope
<bulleted list from the story's Out of Scope section>
## How to verify
<user-facing verification steps a reviewer can run or inspect without reading the code>
## Initiative reference
- Initiative: <initiative-slug>
- Story: <story-slug> — <story title>
- Change workspace: openspec/changes/<story-slug>/
Read the following artifacts in the change workspace and map them to the PR body. Skip any missing artifact silently. This mapping order does not determine the Summary's narrative order: when a catalyst is available, it still comes first.
proposal.md → ## Goal / Context → Summary (catalyst, why now, and outcome when stated)proposal.md → ## External Resources → Original tickets (extract only explicit URLs; omit narrative descriptions)story.md → ## Triggering Need → Summary (catalyst, observable before state, and explicit causal boundaries when stated)story.md → ## Purpose → Summary + Requirementsstory.md → ## Triggering Need, ## Purpose, ## Scope → Original tickets (extract only explicit URLs not already found)story.md → ## Actors → Requirements only when role context is product-facingstory.md → ## Scenarios / Behavior Examples → Acceptance criteria only for normative scenarios linked with exactly one Covers: A<n>; omit orientation-only examplesstory.md → ## Acceptance → Acceptance criteriastory.md → ## Scope → filter for contract-affecting parts only → Contract changesstory.md → ## Out of Scope → Out of scopestory.md → ## Verification → ### Verification Commands → filter for user-facing checks only → How to verifystory.md → ## Verification → ### Test Architecture Plan → exclusion-only proof-planning input; never copy it into the PR bodystory.md → ## Verification → ### Acceptance Proof Matrix → use only to cross-check that acceptance wording is reviewer-verifiable; never copy receipt/proof-ledger rows into the PR bodyspecs/*.md → Contract changes (only where behavioral deltas describe external contract surface changes; summarize in contract language, not implementation language)Before publishing, read the Summary without the title or linked artifacts:
For original ticket/card links:
Do not paste sections verbatim if they contain internal terminology. Rephrase into reviewer-facing language. A reviewer who has never seen the change workspace should understand the PR body.
Exclusion enforcement for PR body generation: never include content from design.md, tasks.md, or progress.md in the PR body. These files may be read only for this command's PR metadata, entry-condition, and approval-evidence gates. Explicitly exclude the implementation review receipt and every feedback receipt/feedback ledger entry, even when nearby product prose is eligible. If a section of story.md is implementation-facing (## Discovery Notes, ## Locked Decisions, ## Implementation Notes, ## Critical Files, story.md → ## Verification → ### Acceptance Proof Matrix, story.md → ## Verification → ### Test Architecture Plan), exclude it from the PR body.
Attach mode (default) — a PR URL is provided:
https://github.com/<org>/<repo>/pull/<n>)gh pr view <PR_URL> --json number,title,headRefName,state,url,body,isDraft,reviewDecision,latestReviews,mergedAt,mergeCommit,closedAt,updatedAt to enrich metadata and read the current body, review decision, merged state, and merge commit.gh pr edit <PR_URL> --body-file <tmpfile>
gh is unavailable, skip enrichment and the body edit, record only what the user provided, and tell the user the PR body was not updatedOpen mode — user passes OPEN=true with no URL, or explicitly selects a ✅ DONE story with no URL and no existing PR was found:
git status is clean or only contains intended changesgh pr create --title "<story title>" --body-file <tmpfile>gh pr view <returned-url> --json number,title,headRefName,state,url,body,isDraft,reviewDecision,latestReviews,mergedAt,mergeCommit,closedAt,updatedAt to populate the same durable metadata fields used by attach/refresh mode.Operator action: check out or create and push the story feature branch, then rerun /openspec-pr <initiative> <story-slug> OPEN=true. For a ✅ DONE story, leave story.md, progress.md, and ## PR State untouched.gh fails or is unavailable, abort with only Operator action: open the PR manually, then rerun /openspec-pr <initiative> <story-slug> <pr-url> in attach mode. Include the failure, and for a ✅ DONE story leave story.md, progress.md, and ## PR State untouched.In both modes, never force-push, never bypass hooks, never rewrite history without explicit user confirmation.
Add or refresh a ## PR State section in <progress_file>:
## PR State
- PR URL: <url>
- Number: <n>
- Title: <pr title>
- Branch: <head ref>
- Opened at: <UTC ISO timestamp>
- PR status: open | changes_requested | approved | merged | closed
- Review decision: <APPROVED | CHANGES_REQUESTED | REVIEW_REQUIRED | blank | unavailable>
- Merge commit: <sha or "—">
- Merged at: <UTC ISO timestamp or "—">
- Verified implementation digest: <receipt Identity digest, or "—" only for accepted pre-v3 no-receipt legacy>
- Verified at: <UTC ISO timestamp from the final pre-mutation identity verification, or "—" only for accepted pre-v3 no-receipt legacy>
- Last synced: <UTC ISO timestamp>
Do not create a duplicate ## PR State section. If one already exists, update its fields in place. When refreshing an older section, add the Review decision:, Merged at:, Verified implementation digest:, and Verified at: fields rather than dropping them. For a modern receipt, the verified digest must exactly equal its Identity digest; never carry forward an older verification timestamp or digest.
When gh pr view is available, derive the progress PR status from the enriched JSON fields, not from the URL alone:
state is MERGED or mergedAt is non-empty, set PR status: merged, set Merged at: to mergedAt when available, and set Merge commit: to mergeCommit.oid (or the user-provided merge commit if gh lacks it).state is CLOSED, set PR status: closed.reviewDecision is CHANGES_REQUESTED, or the latest effective review state in latestReviews is CHANGES_REQUESTED, set PR status: changes_requested.reviewDecision is APPROVED, set PR status: approved.PR status: open.If gh is unavailable in attach mode, record the supplied URL and any user-provided fields, set unavailable fields to unavailable or —, and report that archive cannot treat the PR as merged until live GitHub data or explicit merged-state evidence, merge commit, and merged-at timestamp are recorded.
Append a timestamped bullet under ## Progress Timeline in <progress_file>. Use the entry that matches the operation:
- <UTC ISO timestamp> Opened PR delivery record — <PR URL> (status: <PR status>)
- <UTC ISO timestamp> Attached PR delivery record — <PR URL> (status: <PR status>)
- <UTC ISO timestamp> Refreshed PR delivery record — <PR URL> (status: <PR status>)
Do not write lifecycle transition language in the timeline. This command records delivery metadata only.
Never update the Status: header field in <story_file> from this command. If the entry-condition recheck no longer sees Status: ✅ DONE, abort before writing PR metadata and apply the non-DONE diagnostic routing above; do not retain or print it as resolved PR context.
If ## PR State already has a PR URL, refresh it:
gh pr view --json number,title,headRefName,state,url,body,isDraft,reviewDecision,latestReviews,mergedAt,mergeCommit,closedAt,updatedAt if available and update ## PR State → PR status, Review decision, Merge commit, Merged at, Verified implementation digest, Verified at, and Last synced. The identity fields come only from the final successful pre-mutation recomputation, not cached PR State.PR status is merged and both Merge commit: and Merged at: are populated, report that archive PR evidence is complete.PR status is merged but merge commit or merged-at evidence is missing, report the missing durable evidence and tell the user to rerun /openspec-pr with gh available or provide the missing evidence explicitly before archive.PR status is changes_requested or reviewer comments request changes, do not update story.md → Status:. Tell the user to run /openspec-feedback <initiative> --pr <PR URL> so the feedback can be classified into story rework, planning changes, a follow-up story, initiative decision, or defer/reject.There is no MASTER.md and no tracker table in this flow. PR evidence is written to <progress_file> → ## PR State and <progress_file> → ## Progress Timeline only. No centralized coordination cache is consulted or written, and no story status is changed.
story.md → Status: from this flow. Local completion is owned by /openspec-story-review; PR merge evidence is an archive gate, not story lifecycle authority.gh pr create call in open mode).Last synced stale across PR metadata refreshes.## Current Claim, ## Progress Timeline, ## Session Handoff, design.md, tasks.md, or progress.md content into the PR body. Those sections are implementation diary, not product contract./openspec-feedback.gh data is available. Archive performs its own authoritative preflight.review-identity-v1 from the receipt's recorded bases/path list. Require the recomputed digest to equal the receipt, persist that digest and verification time in PR State, and route mismatch or unverifiable evidence to fresh substantive review. PR State/timeline coordination writes are outside identity scope and require no post-write recomputation.State:
story.md → Status: was left unchangedgh enrichment/body update was usedEnd every success or abort response with:
Suggested next action: <scalar route; leave empty only for a lifecycle dual route>
- Converge wrapper: <command; lifecycle dual routes only>
- Non-looped pass: <state-correct command; lifecycle dual routes only>
Choose one; do not run both.
For a scalar route, put its value on the label line and omit the three dual-route lines. Lifecycle entry failures that legitimately offer wrapper/direct routes leave the label empty and render those three lines immediately after it.
For an eligible locally DONE story, select exactly one exhaustive PR route from current durable/live evidence, in precedence order:
/openspec-pr <initiative> <story-slug> <url> to refresh the same PR record/openspec-archive <initiative> <story-slug>Operator decision: choose whether to reopen the closed PR or explicitly create/attach a replacement PR./openspec-feedback <initiative> --pr <url>isDraft: true) -> Operator decision: keep the PR in draft or mark it ready for review.Wait: PR approved and awaiting merge.Wait for PR review.Also use one scalar route for each open-mode failure: default branch -> check out/create and push the story feature branch, then rerun with OPEN=true; gh unavailable/create failure -> open manually, then rerun with the explicit PR URL in attach mode; operator declines OPEN with no existing PR -> pass an existing PR URL or explicitly rerun with OPEN=true. Do not combine these routes.
isDraft refines only the open-PR next-action message; it does not alter PR status authority or story.md → Status:. Non-DONE candidates are diagnostic only, never resolved context. DONE with non-approved Plan uses only the operator contradiction-reconciliation action. Entry-condition failures use only the state-dependent routing defined above.