LearnNewsExamplesServices
Frontmatter
id10324
titlepr-review must flag PRs that name an epic as close-target — epics close only when all subs resolved
stateClosed
labels
documentationenhancementai
assigneesneo-opus-ada
createdAtApr 25, 2026, 6:03 AM
updatedAtJun 7, 2026, 7:21 PM
githubUrlhttps://github.com/neomjs/neo/issues/10324
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtApr 25, 2026, 6:31 AM

pr-review must flag PRs that name an epic as close-target — epics close only when all subs resolved

Closed v13.0.0/archive-v13-0-0-chunk-6 documentationenhancementai
neo-opus-ada
neo-opus-ada commented on Apr 25, 2026, 6:03 AM

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)

  1. 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).
  2. 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.
  3. 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.
  4. 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

  • .agent/skills/pr-review/references/pr-review-guide.md updated with a new section (suggested location: §5 Required Actions area, or a new §5.2 Close-Target Audit subsection) codifying the rule above.
  • Specific reviewer-side check spelled out: "If PR body contains Closes #N / Resolves #N / Fixes #N AND issue #N has the epic label → 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."
  • Cross-reference the check from the template at .agent/skills/pr-review/assets/pr-review-template.md so reviewers structurally encounter the audit during review composition (e.g., a checkbox in § 🛂 Provenance Audit or a new § 🎯 Close-Target Audit).
  • Empirical anchor cited: this session's #9999 sabotage chain (#9999 closed by neo-gemini-pro 2026-04-23 with 7/10 subs open) referenced inline as the failure mode the rule prevents.
  • No false-positive surface: leaf tickets, regular sub-issues, or tickets without the epic label remain unaffected by the new check.

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.
  • #10302pr-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"

tobiu referenced in commit e6cfd47 - "feat(pull-request): implement author-side epic close-target safeguard (#10324) (#10328) on Apr 25, 2026, 6:59 AM
tobiu referenced in commit c8b12ff - "feat(skills): epic-resolution skill + Substrate Evidence Ladder graduation (#10698) (#10699) on May 4, 2026, 7:20 PM