We are walking the open issue backlog toward zero. During maintainer review of #9958, the issue was closed as superseded by the later #10733 work. That exposed a gap in ticket intake: duplicate and superseded are already allowed verdicts, but the workflow does not make ticket age, stale bot state, or missing PR close-link hygiene a first-class audit input.
#10555 completed the Ticket Reality Classification protocol. It deliberately did not add a backlog sweeper. This ticket keeps that boundary: intake should get sharper when an agent chooses a ticket; it should not spawn a mass sweep.
The repo stale workflow currently marks inactive issues stale after days-before-issue-stale and closes them after days-before-issue-close unless no auto close is present (.github/workflows/close-inactive-issues.yml). At filing time, those values are 90 and 14 days, but the intake rule should read the workflow config rather than hard-code arbitrary bands.
The Problem
Same-day duplicates happen, but the older a ticket is relative to the stale workflow config, the higher the chance that a later ticket, PR, epic decision, discussion, or current source state has resolved or reshaped the premise. Current ticket-intake already asks for duplicate and superseded checks, but it does not require agents to record createdAt, updatedAt, stale, or no auto close, and it does not scale the successor-risk sweep depth by workflow-derived age state before emitting valid-as-written.
A related edge case: a PR can fully implement a ticket but miss the GitHub close keyword (Resolves #N, Closes #N, etc.). GitHub then leaves the issue open even though the correct intake verdict is already-resolved, not new implementation work.
Architectural Reality
.agents/skills/ticket-intake/references/ticket-intake-workflow.md owns the Pre-Execution Reflection Gate.
.github/workflows/close-inactive-issues.yml owns the stale timing and no auto close exemption.
#10555 added the reality verdicts, including duplicate, superseded, and already-resolved, but not age-aware successor-risk handling.
Ticket reality is not equivalent to GitHub auto-close state. A missing close keyword can leave completed work looking open.
ticket-triage may later deserve a narrow extension for never-triaged stale tickets, but that is not part of this ticket.
The Fix
Add an Age / Successor-Risk Audit to ticket-intake, preferably inside Section 1 before Ticket Reality Classification, with a compact required artifact:
Ticket age: created date, updated date, workflow-derived age state, and relevant labels.
Successor-risk sweep: newer tickets, PRs, epics, discussions, and current source/docs/tests checked when the ticket is older, stale, or exempted.
Missing close-link sweep: merged PRs that reference the ticket, touch the target surface, or appear in the issue conversation are checked even when they did not use a GitHub close keyword.
Stale renewal: if stale is present and the ticket remains valid, refresh the issue with a renewal comment and remove stale as intake routine.
Exemption discipline: applying no auto close is not routine intake cleanup. It requires explicit operator intent or a blocked/parked-state rationale.
Verdict integration: age is a risk signal, not a verdict. It raises search depth before valid-as-written.
Suggested workflow-derived states:
pre-stale: issue age/inactivity is below days-before-issue-stale; run the normal duplicate/successor sweep.
stale-window: issue has stale or is at/after days-before-issue-stale but before the stale-plus-close window; run explicit newer-ticket and merged-PR sweeps before valid-as-written.
post-stale-with-exemption: issue remains open beyond days-before-issue-stale + days-before-issue-close, usually because it is exempted or recently refreshed; run the highest-risk audit against newer tickets, PRs, epics, discussions, current source/docs/tests, and merged PRs without close keywords.
Intake records age/stale metadata and scales successor-risk depth before reality verdict
If metadata cannot be fetched, halt with clarification instead of accepting
ticket-intake-workflow.md
Local workflow text check
Workflow-derived age states
.github/workflows/close-inactive-issues.yml
Bands derive from days-before-issue-stale and days-before-issue-close, not arbitrary constants
If config shape changes, cite observed config and use conservative high-risk audit
ticket-intake-workflow.md
Cite workflow lines for stale/close/exemption keys
Ticket Reality Classification artifact
ticket-intake-workflow.md
Classification includes the age audit line before valid-as-written can proceed
Non-valid verdicts route to existing rejection/re-triage paths
Same file
Markdown grep for required artifact fields
Missing PR close-link hygiene
Live issue conversation, linked PRs/commits, current source/docs/tests
A merged PR can produce already-resolved even if it omitted Resolves #N / Closes #N
If evidence is ambiguous, halt with clarification instead of branching
ticket-intake-workflow.md
Evidence lists PR number, merge status, touched surface, and issue/thread link
Stale renewal vs exemption
.github/workflows/close-inactive-issues.yml plus maintainer intent
Valid stale tickets get renewal; no auto close requires explicit parked/blocked rationale
If rationale is unclear, ask maintainer instead of applying exemption
Same workflow plus intake doc
Cite 90/14/no-auto-close config and renewal comment
Acceptance Criteria
ticket-intake-workflow.md adds an explicit Age / Successor-Risk Audit before Ticket Reality Classification.
The classification artifact records createdAt, updatedAt, workflow-derived age state, stale, and no auto close state when available.
The workflow states that age is never a verdict by itself; it only raises successor-risk search depth.
Age bands are derived from .github/workflows/close-inactive-issues.yml values instead of hard-coded arbitrary thresholds.
The audit distinguishes same-day duplicates from older-ticket supersession.
The audit explicitly checks for merged PRs that completed the ticket but missed a GitHub close keyword, routing those cases to already-resolved rather than branch/code work.
Missing close-link evidence must name the PR, prove merged status, identify the target surface touched, and cite the issue conversation or source/docs/tests that establish completion.
Stale renewal and no auto close exemption are separated: renewal is intake routine for still-valid stale tickets; exemption requires explicit parked/blocked rationale.
The stale bot contract is documented with the current config keys for stale, close, and no auto close exemption.
The ticket remains scoped to ticket-intake; no new sweep skill, scheduled backlog sweeper, stale bot timing change, or ticket-triage extension is introduced.
Out of Scope
Automated mass backlog sweeping.
Closing or editing existing tickets as part of this work.
Creating a new skill.
Changing .github/workflows/close-inactive-issues.yml timing or labels.
Extending ticket-triage for never-triaged stale tickets.
Avoided Traps
Do not treat old age as negative ROI by itself.
Do not hard-code age bands that drift from the stale workflow config.
Do not treat open GitHub state as proof that no PR implemented the issue.
Do not use keyword-only mass closure. Superseded, duplicate, and already-resolved findings still need evidence.
Do not bloat ticket-intake/SKILL.md; keep the detailed logic in the referenced workflow file.
Do not auto-apply no auto close as a convenience label.
Handoff Retrieval Hint: ticket-intake age stale no auto close successor risk duplicate superseded older tickets missing close keyword already-resolved merged PR
tobiu referenced in commit bb6f682 - "fix(ai): add successor-risk and enforcement sufficiency audit to ticket-intake (#10758) (#11357) on May 14, 2026, 5:10 PM
Context
We are walking the open issue backlog toward zero. During maintainer review of #9958, the issue was closed as superseded by the later #10733 work. That exposed a gap in ticket intake: duplicate and superseded are already allowed verdicts, but the workflow does not make ticket age, stale bot state, or missing PR close-link hygiene a first-class audit input.
#10555 completed the Ticket Reality Classification protocol. It deliberately did not add a backlog sweeper. This ticket keeps that boundary: intake should get sharper when an agent chooses a ticket; it should not spawn a mass sweep.
The repo stale workflow currently marks inactive issues stale after
days-before-issue-staleand closes them afterdays-before-issue-closeunlessno auto closeis present (.github/workflows/close-inactive-issues.yml). At filing time, those values are 90 and 14 days, but the intake rule should read the workflow config rather than hard-code arbitrary bands.The Problem
Same-day duplicates happen, but the older a ticket is relative to the stale workflow config, the higher the chance that a later ticket, PR, epic decision, discussion, or current source state has resolved or reshaped the premise. Current ticket-intake already asks for duplicate and superseded checks, but it does not require agents to record
createdAt,updatedAt,stale, orno auto close, and it does not scale the successor-risk sweep depth by workflow-derived age state before emittingvalid-as-written.A related edge case: a PR can fully implement a ticket but miss the GitHub close keyword (
Resolves #N,Closes #N, etc.). GitHub then leaves the issue open even though the correct intake verdict isalready-resolved, not new implementation work.Architectural Reality
.agents/skills/ticket-intake/references/ticket-intake-workflow.mdowns the Pre-Execution Reflection Gate..github/workflows/close-inactive-issues.ymlowns the stale timing andno auto closeexemption.duplicate,superseded, andalready-resolved, but not age-aware successor-risk handling.ticket-triagemay later deserve a narrow extension for never-triaged stale tickets, but that is not part of this ticket.The Fix
Add an Age / Successor-Risk Audit to ticket-intake, preferably inside Section 1 before Ticket Reality Classification, with a compact required artifact:
Ticket age: created date, updated date, workflow-derived age state, and relevant labels.Successor-risk sweep: newer tickets, PRs, epics, discussions, and current source/docs/tests checked when the ticket is older, stale, or exempted.Missing close-link sweep: merged PRs that reference the ticket, touch the target surface, or appear in the issue conversation are checked even when they did not use a GitHub close keyword.Stale renewal: ifstaleis present and the ticket remains valid, refresh the issue with a renewal comment and removestaleas intake routine.Exemption discipline: applyingno auto closeis not routine intake cleanup. It requires explicit operator intent or a blocked/parked-state rationale.Verdict integration: age is a risk signal, not a verdict. It raises search depth beforevalid-as-written.Suggested workflow-derived states:
pre-stale: issue age/inactivity is belowdays-before-issue-stale; run the normal duplicate/successor sweep.stale-window: issue hasstaleor is at/afterdays-before-issue-stalebut before the stale-plus-close window; run explicit newer-ticket and merged-PR sweeps beforevalid-as-written.post-stale-with-exemption: issue remains open beyonddays-before-issue-stale + days-before-issue-close, usually because it is exempted or recently refreshed; run the highest-risk audit against newer tickets, PRs, epics, discussions, current source/docs/tests, and merged PRs without close keywords.Contract Ledger Matrix
ticket-intakeValidation Sweepticket-intake-workflow.md.github/workflows/close-inactive-issues.ymldays-before-issue-staleanddays-before-issue-close, not arbitrary constantsticket-intake-workflow.mdticket-intake-workflow.mdvalid-as-writtencan proceedalready-resolvedeven if it omittedResolves #N/Closes #Nticket-intake-workflow.md.github/workflows/close-inactive-issues.ymlplus maintainer intentno auto closerequires explicit parked/blocked rationaleAcceptance Criteria
ticket-intake-workflow.mdadds an explicit Age / Successor-Risk Audit before Ticket Reality Classification.createdAt,updatedAt, workflow-derived age state,stale, andno auto closestate when available..github/workflows/close-inactive-issues.ymlvalues instead of hard-coded arbitrary thresholds.already-resolvedrather than branch/code work.no auto closeexemption are separated: renewal is intake routine for still-valid stale tickets; exemption requires explicit parked/blocked rationale.no auto closeexemption.ticket-intake; no new sweep skill, scheduled backlog sweeper, stale bot timing change, orticket-triageextension is introduced.Out of Scope
.github/workflows/close-inactive-issues.ymltiming or labels.ticket-triagefor never-triaged stale tickets.Avoided Traps
ticket-intake/SKILL.md; keep the detailed logic in the referenced workflow file.no auto closeas a convenience label.Related
.github/workflows/close-inactive-issues.ymlOrigin Session ID: 2821a9d4-7634-4fe7-a8a2-daf49253b929
Handoff Retrieval Hint:
ticket-intake age stale no auto close successor risk duplicate superseded older tickets missing close keyword already-resolved merged PR