fix(review): require a genuinely open issue for the linked-issue block gate - #3514
Conversation
…k gate pr.linkedIssues is populated by a pure body-text regex match and never checked whether the cited issue is actually open, so linkedIssueGateMode: "block" could be satisfied by citing an already-closed or fabricated issue number. The missing_linked_issue blocker now also fires when every citation resolves to confirmed-closed or not-found, fail-open on any ambiguous fetch so a transient GitHub error never wrongly blocks a legitimately linked PR.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## claude/zealous-herschel-f6bfba #3514 +/- ##
=================================================================
Coverage ? 93.03%
=================================================================
Files ? 301
Lines ? 31160
Branches ? 11366
=================================================================
Hits ? 28990
Misses ? 1513
Partials ? 657
🚀 New features to boost your workflow:
|
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-05 15:23:47 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 4 non-blocking
Concerns raised — review before merging
Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
…k gate (#3514) pr.linkedIssues is populated by a pure body-text regex match and never checked whether the cited issue is actually open, so linkedIssueGateMode: "block" could be satisfied by citing an already-closed or fabricated issue number. The missing_linked_issue blocker now also fires when every citation resolves to confirmed-closed or not-found, fail-open on any ambiguous fetch so a transient GitHub error never wrongly blocks a legitimately linked PR.
…k gate (#3514) pr.linkedIssues is populated by a pure body-text regex match and never checked whether the cited issue is actually open, so linkedIssueGateMode: "block" could be satisfied by citing an already-closed or fabricated issue number. The missing_linked_issue blocker now also fires when every citation resolves to confirmed-closed or not-found, fail-open on any ambiguous fetch so a transient GitHub error never wrongly blocks a legitimately linked PR.
* feat(review): hold PRs that solve an unlinked open issue Adds an opt-in, off-by-default guardrail: when a contributor PR links no issue, a cheap deterministic pre-filter looks for open issues whose title/body or changed-file paths overlap it, and any candidate is verified by the free/self-host AI reviewer (never BYOK) before the PR is held for manual review. Never auto-closes and never blocks a PR that has nothing to match against -- only confirmed, high-confidence matches route to a hold, so a maintainer can catch a contributor slicing an issue across unlinked PRs to farm merge-ratio credibility without the scrutiny a linked issue would invite. * fix(review): require a genuinely open issue for the linked-issue block gate (#3514) pr.linkedIssues is populated by a pure body-text regex match and never checked whether the cited issue is actually open, so linkedIssueGateMode: "block" could be satisfied by citing an already-closed or fabricated issue number. The missing_linked_issue blocker now also fires when every citation resolves to confirmed-closed or not-found, fail-open on any ambiguous fetch so a transient GitHub error never wrongly blocks a legitimately linked PR. * feat(review): escalate a confirmed repeat unlinked-issue match to a close A first confirmed match still only holds the PR for manual review. A second confirmed match by the SAME contributor (tracked via the existing audit_events ledger, hasRecentAuditEvent/recordAuditEvent) is no longer a coincidence worth the benefit of the doubt, so it closes the PR one-shot instead. The close stays tagged closeKind: heuristic (not concrete evidence) since the underlying signal is an AI semantic-match verdict, keeping it subject to the close-precision breaker even after it repeats. * fix(review): hold instead of merge a confirmed unlinked-issue repeat when close is disabled heldForManualReview didn't account for unlinkedIssueMatchClose, so a confirmed repeat could still satisfy canMerge with close autonomy off. Also closes the remaining codecov/patch branch gaps: the linked-issue-hard-rule reason fallback and the unlinkedIssueGuardrail minConfidence-only sparse override.
Summary
mainautomaticallyonce feat(review): hold PRs that solve an unlinked open issue #3513 merges).
pr.linkedIssuesis populated by a pure body-text regex match (extractLinkedIssueNumbersWithOverflow)that never checks whether the cited issue is actually open. A repo running
linkedIssueGateMode: "block"(requires a linked issue to merge) could therefore be satisfied by a contributor citing an already-CLOSED
or fabricated issue number — completely defeating the point of requiring a link.
missing_linked_issuenow also fires when every one of a PR's citations resolves to confirmed-closed orconfirmed-not-found, with live verification (
src/review/linked-issue-hard-rules.ts's newresolveLinkedIssueHasOpenReference/hasVerifiableOpenLinkedIssueReference, mirroring the existingresolveLinkedIssueHardRule's fail-open/fail-closed discipline: a single ambiguous fetch error stillfails open, only a fully-confirmed-dead set of citations trips the blocker).
main webhook path, the cron re-gate sweep, the heavy re-review pass, and authorized PR actions
(gate-override / panel retrigger) — via one shared resolver,
resolveLinkedIssueAdvisoryContextinsrc/queue/processors.ts.pr.linkedIssuesitself is unchanged everywhere else it's used (duplicate-winner overlap, labelpropagation, scoring) — only the
missing_linked_issuegate check gained the extra verification.Scope
type(scope): short summaryConventional Commit format.CONTRIBUTING.md; nosite//CNAME/VitePress changes.Validation
git diff --checknpm run actionlint— not run; no workflow files touched.npm run typecheck(clean, in a fresh worktree built from this branch)npm run test:coverage(full/unsharded) — not run locally; ran the full affected-file set instead(
linked-issue-hard-rules.test.ts,rules.test.ts,agent-actions.test.ts,focus-manifest.test.ts,and the full
queue.test.ts, 511 tests) in a cleannpm ciworktree — all 1114 tests green, and100% branch coverage confirmed on every new/changed line via targeted
--coverage.includerunsagainst
coverage-final.json. GitHub CI runs the full suite/gate on push.npm run test:workers/npm run build:mcp/npm run test:mcp-pack— not run; nothing in thosesurfaces touched.
npm run ui:openapi:check/ui:lint/ui:typecheck/ui:build— not run; no OpenAPI schema orapps/gittensory-ui/**changes in this PR.npm audit --audit-level=moderate— not run; no dependency changes.confirmed-open / confirmed-dead / ambiguous / mixed cases, the live-fetch orchestrator's token-mint
and fallback paths, the advisory's new
confirmedNoOpenLinkedIssuefinding text and its interactionwith the zero-citation case, and two end-to-end
processJobtests proving the actual regression: aPR citing a closed issue under
linkedIssueGateMode: blocknow fails the gate, and a PR citing agenuinely open one still passes.
If any required check was skipped, explain why:
npm ciworktreerather than reusing a possibly-stale one) rather than a full repo-wide
test:ci/npm auditpass, sinceGitHub CI runs the complete gate on push.
Safety
detailtext and thenew trigger condition changed) — no new public-facing text pattern to sanitize.
UI Evidencesection included.Notes
state must never wrongly block a PR that's genuinely, correctly linked to real open work — only a fully
confirmed-dead set of citations trips the new check.