Goal
Enforce the 1-PR-per-ticket model at the CI gate. Operator-directed (2026-06-02): every agent/ai-labeled PR body MUST contain at least one Resolves #N — the only sanctioned closing keyword (= delivered work). This mechanically prevents ticket-fragmentation: a ticket that needs N PRs can't have N valid Resolves (only one PR can resolve it), so it's forced to be an epic + subs or split into multiple tickets. Empirical trigger: ~27 PRs were opened for the single ticket #11925 (all Refs #11925, none resolving it) — the current check accepts Refs/Related, so it didn't catch the fragmentation.
The rule (exact, operator-specified)
- MUST contain ≥1
Resolves #N — the only accepted closing keyword (delivered work).
Closes #N is forbidden — Closes means closed-without-delivery (not-planned / superseded / dropped); that needs no PR, so a PR must never use it.
Fixes #N is forbidden — ambiguous; use Resolves.
Refs #N / Related: #N are allowed as additional references, and multiple Resolves lines are fine — but they do not satisfy the requirement; ≥1 Resolves is mandatory.
Scope
.github/workflows/agent-pr-body-lint.yml — replace the permissive ticket regex (Resolves|Closes|Fixes|Related:|Refs)\s+#\d+ with: require ≥1 Resolves\s+#\d+; fail if Closes/Fixes #N appears; update the violation message.
.agents/skills/pull-request/references/pull-request-workflow.md §3.3 — sync the close-keyword policy to "Resolves-only; ≥1 mandatory; Closes/Fixes forbidden; Refs/Related allowed as additional" and reconcile the partial-resolution guidance (a contributing PR Resolves its own sub-ticket and Refs the parent — it never resolves nothing).
Contract Ledger
| Surface |
Input |
Rule |
Output |
agent-pr-body-lint workflow |
agent/ai PR body |
≥1 Resolves #N; no Closes/Fixes #N; Refs/Related + extra Resolves allowed |
pass, or fail + guidance comment |
Acceptance Criteria
- PR body with ≥1
Resolves #N (plus optional extra Refs/Resolves) passes.
- PR body with only
Refs/Related (no Resolves) fails.
- PR body containing
Closes #N or Fixes #N fails with the explanation (Closes = no-delivery/no-PR; Fixes = ambiguous → use Resolves).
- Non-agent, non-
ai PRs remain skipped (unchanged).
pull-request-workflow.md §3.3 wording matches the enforced rule.
Related: #11501 (original agent PR-body lint).
Goal
Enforce the 1-PR-per-ticket model at the CI gate. Operator-directed (2026-06-02): every agent/
ai-labeled PR body MUST contain at least oneResolves #N— the only sanctioned closing keyword (= delivered work). This mechanically prevents ticket-fragmentation: a ticket that needs N PRs can't have N validResolves(only one PR can resolve it), so it's forced to be an epic + subs or split into multiple tickets. Empirical trigger: ~27 PRs were opened for the single ticket #11925 (allRefs #11925, none resolving it) — the current check acceptsRefs/Related, so it didn't catch the fragmentation.The rule (exact, operator-specified)
Resolves #N— the only accepted closing keyword (delivered work).Closes #Nis forbidden —Closesmeans closed-without-delivery (not-planned / superseded / dropped); that needs no PR, so a PR must never use it.Fixes #Nis forbidden — ambiguous; useResolves.Refs #N/Related: #Nare allowed as additional references, and multipleResolveslines are fine — but they do not satisfy the requirement; ≥1Resolvesis mandatory.Scope
.github/workflows/agent-pr-body-lint.yml— replace the permissive ticket regex(Resolves|Closes|Fixes|Related:|Refs)\s+#\d+with: require ≥1Resolves\s+#\d+; fail ifCloses/Fixes #Nappears; update the violation message..agents/skills/pull-request/references/pull-request-workflow.md§3.3 — sync the close-keyword policy to "Resolves-only; ≥1 mandatory; Closes/Fixes forbidden; Refs/Related allowed as additional" and reconcile the partial-resolution guidance (a contributing PRResolvesits own sub-ticket andRefsthe parent — it never resolves nothing).Contract Ledger
agent-pr-body-lintworkflowaiPR bodyResolves #N; noCloses/Fixes #N;Refs/Related+ extraResolvesallowedAcceptance Criteria
Resolves #N(plus optional extraRefs/Resolves) passes.Refs/Related(noResolves) fails.Closes #NorFixes #Nfails with the explanation (Closes = no-delivery/no-PR; Fixes = ambiguous → use Resolves).aiPRs remain skipped (unchanged).pull-request-workflow.md§3.3 wording matches the enforced rule.Related: #11501 (original agent PR-body lint).