feat(issues): respect declined contributor-issue drafts - #518
Conversation
generateContributorIssueDrafts re-proposed (and in create mode re-created) drafts a maintainer had already closed, since dedup only considered open issues. Add a bounded closed-marked-issue lister and findDeclinedContributorDraft: a closed issue carrying a draft's stable marker suppresses re-proposal (skipped_declined) -- indefinitely for wontfix-style labels, otherwise within a 30-day cooldown so genuine regressions can resurface.
|
Note Gittensory Gate skippedPR closed before full evaluation. No late first comment was created.
Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers. |
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
… gate Mark the defensive declinedIssues [] fallback (unreachable via loadContributorIssueDraftContext) with v8 ignore, and add a findDeclinedContributorDraft case for a missing close timestamp, restoring branch coverage above the 97% gate.
After merging main, the self-repo (JSONbored/gittensory) now carries a bundled focus manifest (#389), changing its generated draft, so the declined-draft generation test no longer matched its hardcoded policy fingerprint. Use a non-self repo (other-owner/other-repo) -- as the sibling duplicate-draft test does -- so the focus_policy_missing candidate is deterministic.
JSONbored
left a comment
There was a problem hiding this comment.
Approving. This correctly closes the trust-eroding re-proposal loop from #517 and is well-tested.
Root cause is accurate: generateContributorIssueDrafts deduped only against open issues (findDuplicateContributorDraft skips non-open; loadContributorIssueDraftContext loaded only listOpenIssues), so a maintainer who closed a generated draft saw it re-proposed — and in create mode re-created — on every run, since policy:* / upstream:registry_drift fingerprints are stable.
The change is clean and additive:
listClosedContributorDraftIssuesis bounded (limit 200), recent-first, and filtered by the draft marker inpayloadJson.findDeclinedContributorDraftmatches by the stable marker fingerprint; a wontfix-style label suppresses indefinitely, otherwise a 30-day cooldown is honored so a genuine later regression can resurface. The closed-issue check is layered after the open-duplicate check, preserving the existing open-only semantics.- New
skipped_declinedstatus +declinedByprovenance + counter are threaded through consistently.
Tests cover the marker / wontfix / cooldown-boundary / open / no-marker / other-fingerprint matrix plus an end-to-end generation test asserting skipped_declined, created: 0, and no GitHub POST. Backward compatible (no closed marked issues -> unchanged output). CI green.
Minor note (non-blocking): findDeclinedContributorDraft keys the cooldown off updatedAt as a proxy for closed-at; if an issue is closed and later edited, updatedAt moves forward and could extend the cooldown. Acceptable for this use, but a closedAt field would be more precise if one is available later.
|
reviewbot · verdict: Reviewed 3 changed file(s) — two independent AI reviewers: Reviewer A · Suggestions
Worth double-checking
Reviewer B · Suggestions
Worth double-checking
|
Closes #517.
generateContributorIssueDrafts(#462) deduped only against open issues (findDuplicateContributorDraftskipsstate !== "open";loadContributorIssueDraftContextloadslistOpenIssues). So a maintainer who closed a generated draft saw it re-proposed — and increatemode re-created — on every run while the originating warning persisted (fingerprints are stable forpolicy:*/upstream:registry_drifttopics). That eroded trust in the automation by re-filing declined work.Changes
db/repositories.ts: addlistClosedContributorDraftIssues(env, repo, markerPrefix, limit=200)— bounded, recent-first, filters closed issues whosepayloadJsoncarries a contributor-draft marker.contributor-issue-draft.ts:findDeclinedContributorDraft(closedIssues, draft, { now?, cooldownMs? })— matches by the stable marker fingerprint; a wontfix-style label (wontfix/invalid/duplicate/not-planned) suppresses re-proposal indefinitely, otherwise the closure is honored within a 30-day cooldown so a genuine later regression can resurface.skipped_declinedstatus +declinedByprovenance +skippedDeclinedresult counter; loaddeclinedIssuesin the context and short-circuit after the open-duplicate check.findDuplicateContributorDraftare preserved (closed-issue handling is a separate, additive check).Verification
findDeclinedContributorDraft(marker / wontfix / cooldown boundary / open / no-marker / other-fingerprint) and a generation test (closed marked issue ->skipped_declined,created: 0, no GitHub POST).tsc --noEmitclean; full unit suite green (1330 passed).