Skip to content

fix(review): deterministic linked-issue gate never checks issue-PR scope match, only existence/openness #3906

Description

@JSONbored

Context

Repro: JSONbored/metagraphed PR #3910 (real-venus, "feat(registry): add SN74 Gittensor per-repo commits subnet-api surface (#1275)"). Issue #1275 asks to enrich SN74 Gittensor with an sse surface ("Enrich SN74 Gittensor — add SSE stream"). The PR instead adds a subnet-api REST surface for a different endpoint. The AI reviewer correctly wrote, in the comment's prose "Blockers" section: "The PR claims to close #1275, but the linked issue is scoped to adding an SSE stream for SN74 Gittensor, not a subnet-api REST endpoint — the added surface does not satisfy that issue's scope."

Yet the same posted comment's structured "Linked issue" signal row read ✅ Linked · #1275, Gate result: ✅ Passing · No configured blocker found, and the top-line disposition was approve/merge recommended.

Root cause

addPullRequestFindings (src/rules/advisory.ts:697-698) derives the missing_linked_issue finding purely from existence/openness:

const noLinkedIssueCited = pr.linkedIssues.length === 0;
if ((noLinkedIssueCited || confirmedNoOpenLinkedIssue) && requireLinkedIssue) { ... }

There is no check anywhere in this deterministic path for whether the linked issue's scope actually matches the PR's delivered content. A PR that cites a real, open, but unrelated issue passes this exactly as if it cited a perfectly matching one.

This is a distinct gap from three already-closed issues in the same area — filing this fresh rather than reopening any of them:

None of those cover "the linked issue exists, is open, but doesn't match the PR's content" — exactly the case metagraphed's own contributor policy calls out (a linked issue must genuinely match the PR, not just exist/be open), but which the deterministic gate today has no way to detect.

Requirements

  • Add a scope-match dimension to linked-issue evaluation, gated per-repo via .gittensory.yml (e.g. gate.linkedIssue: "scope-strict"), default unchanged/off everywhere that doesn't opt in.
  • When enabled, and the AI reviewer has flagged (with sufficient confidence, i.e. it placed the finding under "Blockers" not "Nits") that the cited issue's scope doesn't match the PR's delivered content, fire a linked_issue_scope_mismatch finding, demote the "Linked issue" signal row off ✅ Linked, and prevent decision: merge.
  • Do not change default behavior for gittensory or any repo that hasn't opted in — this is additive/config-gated, not a revisit of the fix(review): decide whether AI-judgment blockers should gate the verdict, not just annotate it #2592 universal-AI-blocker decision.
  • Regression test reproducing the fix(review): preserve gate verdict on auto-review skip #3910 shape (an open, cited issue whose scope doesn't match the PR body/diff): confirm the new signal fires when a repo opts in, and stays inert when it doesn't.

Deliverables

  • New linked_issue_scope_mismatch finding code, config-gated per-repo
  • .gittensory.yml schema + resolver + openapi coverage for the new gate knob (same PR, per the existing gate-setting-wiring template)
  • Regression test reproducing metagraphed PR fix(review): preserve gate verdict on auto-review skip #3910's shape
  • Docs: CONTRIBUTING/skill note that scope-strict repos require the linked issue to genuinely match, not just exist

Expected outcomes

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions