Repository navigation
chore: auto-close linked issues when PRs merge into dev #519
Description
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 bygithub.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev'. Merges tomainkeep GitHub's native auto-close — no overlap. - No
actions/checkout: the job is pure event-payload +ghAPI 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
#nrefs. Qualified (owner/repo#n) and cross-repo URL refs are out of scope — SpecGit writes plainCloses #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 completedwith a comment naming the delivery PR and explaining the dev-layer auto-close mirror. - Follow repo workflow conventions:
# ====header block (Purpose/Trigger/Jobs/Notes), emojiname:.
Acceptance
actionlintclean on the new file; embedded script passesshellcheck.- Parse-pipeline dry-run against real merged PR bodies extracts exactly the expected issue numbers: fix(test): bound httpapi exerciser teardown and force process exit (#472) #487→{472}, feat(opencode): add warn-only shell silence guard #491→{433}, chore: sync main to dev after v1.0.37 notes fix #497→{496}, chore: sync dev to main for v1.0.39 #518→{517}; a body referencing a PR number skips it.
- Delivery PR passes the
devgate (Typecheck + Unit Tests (linux)) and merges;specgit finishexits 0. - Live proof: the next PR merged into
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.
Activity
[-]ci: auto-close linked issues when PRs merge into dev[/-][+]chore: auto-close linked issues when PRs merge into dev[/+]LeXwDeX commentedon Sep 3, 2026
SpecGit delivery branch:
feat/519-auto-close-linked(draft pull request #520).github-actions commentedon Sep 3, 2026
Auto-closed: delivery PR #520 (view) merged into
devwith a closing keyword for #519 in its body. GitHub's native auto-close only fires on the default branch (main); this mirrors it for the dev integration layer (#519).