Why
GitHub auto-closes an issue only when a PR carrying Closes #n merges into the default branch (main). This repo's two-tier Git Workflow delivers into dev first (promotion to main happens later via release-sync PRs that carry no per-issue references), so delivery PRs never fire auto-close. Proven by #433/#472/#473/#474/#475/#477/#496: all delivered by merged PRs with intact Closes #n bodies (PRs #480/#482/#485/#486/#487/#491/#497), all left open, all closed manually on 2026-09-03. SpecGit's own #347 closure gate knows this failure mode but only refuses the next bootstrap — it never closes the issues.
Scope
One new GitHub Actions workflow: .github/workflows/dev-issue-autoclose.yml. No product code, no changes to existing workflows (specgit-accept.yml local specializations untouched), no policy changes.
Approach
- Trigger
pull_request: types: [closed]; job guarded by github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev'. Merges to main keep GitHub's native auto-close — no overlap.
- No
actions/checkout: the job is pure event-payload + gh API work (also keeps the CodeQL taint surface empty).
- Least-privilege
permissions: contents: read, issues: write, pull-requests: read.
- Parse the PR body for the official GitHub closing keywords (close/closes/closed/fix/fixes/fixed/resolve/resolves/resolved, case-insensitive) followed by bare
#n refs. Qualified (owner/repo#n) and cross-repo URL refs are out of scope — SpecGit writes plain Closes #n.
- Injection safety: the PR body reaches the script only through
env:, never inline ${{ }} interpolation.
- Guards for the shared issue/PR number space: skip numbers that resolve to a pull request, skip nonexistent numbers, skip already-CLOSED issues (no duplicate comments).
- Close via
gh issue close <n> --reason completed with a comment naming the delivery PR and explaining the dev-layer auto-close mirror.
- Follow repo workflow conventions:
# ==== header block (Purpose/Trigger/Jobs/Notes), emoji name:.
Acceptance
Why
GitHub auto-closes an issue only when a PR carrying
Closes #nmerges into the default branch (main). This repo's two-tier Git Workflow delivers intodevfirst (promotion tomainhappens later via release-sync PRs that carry no per-issue references), so delivery PRs never fire auto-close. Proven by #433/#472/#473/#474/#475/#477/#496: all delivered by merged PRs with intactCloses #nbodies (PRs #480/#482/#485/#486/#487/#491/#497), all left open, all closed manually on 2026-09-03. SpecGit's own #347 closure gate knows this failure mode but only refuses the next bootstrap — it never closes the issues.Scope
One new GitHub Actions workflow:
.github/workflows/dev-issue-autoclose.yml. No product code, no changes to existing workflows (specgit-accept.ymllocal specializations untouched), no policy changes.Approach
pull_request: types: [closed]; job guarded bygithub.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev'. Merges tomainkeep GitHub's native auto-close — no overlap.actions/checkout: the job is pure event-payload +ghAPI work (also keeps the CodeQL taint surface empty).permissions:contents: read,issues: write,pull-requests: read.#nrefs. Qualified (owner/repo#n) and cross-repo URL refs are out of scope — SpecGit writes plainCloses #n.env:, never inline${{ }}interpolation.gh issue close <n> --reason completedwith a comment naming the delivery PR and explaining the dev-layer auto-close mirror.# ====header block (Purpose/Trigger/Jobs/Notes), emojiname:.Acceptance
actionlintclean on the new file; embedded script passesshellcheck.devgate (Typecheck + Unit Tests (linux)) and merges;specgit finishexits 0.devwith aCloses #nbody gets its issue auto-closed. This delivery's own merge is expected to fire the new workflow (same-repopull_requestevents evaluate workflows at the merge ref) and close this issue — the self-fire is the acceptance observation, not a gate input.