Author's Note: Filed by Claude Opus 4.7 (Claude Code) during session b5a17132-7324-46e1-b73e-038825bb4d55 at @tobiu's request after empirical sabotage of Epic #9999 surfaced this gap in pr-review discipline.
Context
Epic #9999 ("Cloud-Native Knowledge & Multi-Tenant Memory Core") was closed at 2026-04-23T23:54:09Z by @neo-gemini-pro with stateReason: COMPLETED, despite only 3 of 10 sub-issues being resolved (7 still open). The most likely mechanism: a merged PR named Closes #9999 or Resolves #9999 in its body, triggering GitHub's auto-close-on-merge behavior. This was downstream-amplified by the prevent-reopen.yml workflow (now disabled_manually per direct cleanup this session) which re-closed @tobiu's reopen 6 seconds after his manual fix attempt.
The architectural rule @tobiu surfaced after the cleanup:
"An epic must never be a close target for PRs, since an epic must have subs. Once all subs are resolved, we can close an epic."
This rule isn't currently codified anywhere in .agent/skills/pr-review/. PR reviews don't structurally check whether a PR's stated close-target is itself an epic that has unresolved sub-issues. When the rule isn't enforced at review time, it leaks into merge → auto-close → epic-closed-with-open-subs → downstream work-tracking corruption.
The Problem
GitHub's "magic close keyword" semantics (Closes #N, Resolves #N, Fixes #N in a PR body or commit message) auto-close the referenced issue on merge — regardless of whether #N is a leaf ticket or an epic with open children. PR review currently doesn't audit close-targets against issue labels, so an epic-mistaken-as-leaf goes through cleanly.
Compounding factor: the prevent-reopen.yml workflow's 24h grace period failed to apply to #9999 (original close was 28h before @tobiu's reopen attempt), so the workflow re-closed AND forked the ticket into a duplicate (#10323, also closed this session as sabotage spawn). Even with the workflow now disabled_manually, the upstream root cause — pr-review not catching epic-as-close-target — remains.
The Architectural Rule (to codify in pr-review-guide)
- Epics have sub-issues by definition. An epic without subs is mis-labeled as
epic (or it's a leaf ticket masquerading as an epic).
- PRs close subs, not epics. A PR's unit of deliverable is a sub-issue (or unlabeled feature/bug ticket). PRs should contribute toward an epic, not close it.
- Epics close when their last sub closes (or when the epic is explicitly retired with rationale). Epic-close is a project-management event, not a PR-merge side effect.
- PR close-target audit is a pr-review structural check. Reviewers must verify: for every issue named in
Closes/Resolves/Fixes, that issue does NOT carry the epic label.
Acceptance Criteria
Out of Scope
prevent-reopen.yml permanent removal. Distinct concern (workflow that compounds the damage by re-closing reopens). Already disabled_manually this session; durable cleanup-via-PR is a separate ticket.
- Auto-detection at PR creation time. Could imagine a GH Actions workflow that flags epic-as-close-target before review begins. Out of scope for this ticket — the discipline-layer fix (pr-review skill) is sufficient and more robust to model variation. Workflow-layer enforcement could be a follow-up if discipline alone proves insufficient.
- Auditing existing closed epics for incorrect-close-via-PR. Retrospective audit is a different scope (forensic, not preventive). #9999 cleanup landed this session; further audit would be ad-hoc work.
- Generalizing the rule beyond epics. Some workflows legitimately close one ticket via a PR that closes another (e.g., consolidation tickets). The
epic-label check is the precise discriminator; broadening it would create false positives.
Avoided Traps
- Trying to enforce via GH Actions only. Workflows can be disabled, bypassed via direct push, or fail silently. Discipline-layer enforcement (pr-review skill) is durable and visible to reviewers.
- Generalizing to "no PR closes any parent ticket." Rejected — sub-issues with their own children (rare but legitimate) wouldn't be the same risk class. The discriminator is
epic-label specifically.
- Auto-rewriting the PR body. Rejected — the reviewer flags as Required Action; the author makes the choice (change close-target to specific sub vs remove close-target). Author-side autonomy matters per
pr-review §6.1 cross-family discipline.
- Conflating the close-target rule with
prevent-reopen.yml behavior. Rejected — these are independent concerns. The close-target rule prevents incorrect closes; prevent-reopen.yml (now disabled) was an amplifier of the original error. Fixing one doesn't depend on the other.
Related
- #9999 — Empirical anchor; the epic that was sabotage-closed this session. Now reopened.
- #10303 — Recent
pr-review skill enhancement (Provenance Audit). Architectural neighbor; same skill being extended.
- #10302 —
pr-review decile-anchor scoring rubric. Adjacent template-layer work.
- #10311 — Epic that itself depends on this rule being enforced; if its sub PRs close
#10311 instead of specific subs, the same sabotage pattern recurs.
prevent-reopen.yml — Workflow now disabled_manually after compounding the #9999 sabotage. Separate ticket may follow for permanent removal.
Origin Session ID: b5a17132-7324-46e1-b73e-038825bb4d55
Retrieval Hint: "pr-review epic close-target audit Closes #N Resolves #N magic-keyword sub-issue not-yet-resolved auto-close-on-merge sabotage-prevention skill-enhancement"
Context
Epic #9999 ("Cloud-Native Knowledge & Multi-Tenant Memory Core") was closed at 2026-04-23T23:54:09Z by @neo-gemini-pro with
stateReason: COMPLETED, despite only 3 of 10 sub-issues being resolved (7 still open). The most likely mechanism: a merged PR namedCloses #9999orResolves #9999in its body, triggering GitHub's auto-close-on-merge behavior. This was downstream-amplified by theprevent-reopen.ymlworkflow (nowdisabled_manuallyper direct cleanup this session) which re-closed @tobiu's reopen 6 seconds after his manual fix attempt.The architectural rule @tobiu surfaced after the cleanup:
This rule isn't currently codified anywhere in
.agent/skills/pr-review/. PR reviews don't structurally check whether a PR's stated close-target is itself an epic that has unresolved sub-issues. When the rule isn't enforced at review time, it leaks into merge → auto-close → epic-closed-with-open-subs → downstream work-tracking corruption.The Problem
GitHub's "magic close keyword" semantics (
Closes #N,Resolves #N,Fixes #Nin a PR body or commit message) auto-close the referenced issue on merge — regardless of whether #N is a leaf ticket or an epic with open children. PR review currently doesn't audit close-targets against issue labels, so an epic-mistaken-as-leaf goes through cleanly.Compounding factor: the
prevent-reopen.ymlworkflow's 24h grace period failed to apply to #9999 (original close was 28h before @tobiu's reopen attempt), so the workflow re-closed AND forked the ticket into a duplicate (#10323, also closed this session as sabotage spawn). Even with the workflow nowdisabled_manually, the upstream root cause — pr-review not catching epic-as-close-target — remains.The Architectural Rule (to codify in pr-review-guide)
epic(or it's a leaf ticket masquerading as an epic).Closes/Resolves/Fixes, that issue does NOT carry theepiclabel.Acceptance Criteria
.agent/skills/pr-review/references/pr-review-guide.mdupdated with a new section (suggested location:§5Required Actions area, or a new§5.2 Close-Target Auditsubsection) codifying the rule above.Closes #N/Resolves #N/Fixes #NAND issue #N has theepiclabel → flag as Required Action: change close-target to a sub-issue, or remove the close-target if the PR doesn't fully resolve the epic. Auto-close-on-merge must not fire on epic-labeled issues.".agent/skills/pr-review/assets/pr-review-template.mdso reviewers structurally encounter the audit during review composition (e.g., a checkbox in§ 🛂 Provenance Auditor a new§ 🎯 Close-Target Audit).neo-gemini-pro2026-04-23 with 7/10 subs open) referenced inline as the failure mode the rule prevents.epiclabel remain unaffected by the new check.Out of Scope
prevent-reopen.ymlpermanent removal. Distinct concern (workflow that compounds the damage by re-closing reopens). Alreadydisabled_manuallythis session; durable cleanup-via-PR is a separate ticket.epic-label check is the precise discriminator; broadening it would create false positives.Avoided Traps
epic-label specifically.pr-review §6.1cross-family discipline.prevent-reopen.ymlbehavior. Rejected — these are independent concerns. The close-target rule prevents incorrect closes;prevent-reopen.yml(now disabled) was an amplifier of the original error. Fixing one doesn't depend on the other.Related
pr-reviewskill enhancement (Provenance Audit). Architectural neighbor; same skill being extended.pr-reviewdecile-anchor scoring rubric. Adjacent template-layer work.#10311instead of specific subs, the same sabotage pattern recurs.prevent-reopen.yml— Workflow nowdisabled_manuallyafter compounding the #9999 sabotage. Separate ticket may follow for permanent removal.Origin Session ID:
b5a17132-7324-46e1-b73e-038825bb4d55Retrieval Hint:"pr-review epic close-target audit Closes #N Resolves #N magic-keyword sub-issue not-yet-resolved auto-close-on-merge sabotage-prevention skill-enhancement"