Skip to content

fix(review): treat a confirmed-nonexistent linked issue as a hard-rule violation#2402

Merged
JSONbored merged 2 commits into
mainfrom
claude/linked-issue-nonexistent-tristate
Jul 2, 2026
Merged

fix(review): treat a confirmed-nonexistent linked issue as a hard-rule violation#2402
JSONbored merged 2 commits into
mainfrom
claude/linked-issue-nonexistent-tristate

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

What

fetchLinkedIssueFacts (src/github/backfill.ts) collapsed a confirmed 404 (issue genuinely doesn't exist) and a transient fetch failure (network/5xx/rate-limit) into the same undefined result. resolveLinkedIssueHardRule then treated an all-undefined fetch batch as "no violation" (fail open) regardless of why every fetch came back empty. So a contributor citing a fabricated issue number (Fixes #999999) satisfied the "has a linked issue" advisory check and was fully exempt from the ownerAssignedClose/missingPointLabelClose/maintainerOnlyLabelClose hard rules — even on a repo with all three set to block. The codebase already closed this exact evasion class for reference overflow (too many closing refs = a violation, not a silent skip); the same principle was never applied to the all-404 case, and there was even an existing test explicitly titled "is fail-open: undefined when every fetch fails (404)" documenting the gap.

Fix

  • fetchLinkedIssueFacts now returns a tri-state result — { status: "found", facts } / { status: "not_found" } / { status: "fetch_error" } — instead of collapsing both 404 and transient errors into undefined. Distinguishes via the already-thrown GitHubApiError's statusCode (same-module class, no export needed) rather than a blind .catch(() => undefined).
  • resolveLinkedIssueHardRule (src/review/linked-issue-hard-rules.ts) now treats an ALL-not_found fetch batch the same way it already treats overflow: an unsafe-to-verify violation, with a reason citing that the reference couldn't be found. A single fetch_error anywhere in the mix still fails open (the same way as before) — we can't rule out a real, rule-violating issue hiding behind a genuine outage.
  • Updated the other call site (resolveLinkedIssueAuthorLogins in src/queue/processors.ts) for the new tri-state shape; its outer .catch(() => null) was removed since fetchLinkedIssueFacts no longer rejects (the pure .then() callback that replaced it can't throw either).

Deliberately out of scope: the issue's "Consider hardening the missing_linked_issue advisory finding similarly" suggestion — that finding is a synchronous, pure regex-count check with no live GitHub fetch today, and extending it would be a separate, larger architectural change (making an otherwise-pure advisory path async). Left for a follow-up if desired.

Tests

  • Renamed/repurposed the existing "is fail-open: undefined when every fetch fails (404)" test (the exact test the issue calls out as documenting the bug) to assert violated: true instead — the corrected behavior.
  • New test: a genuine transient failure (5xx) still fails open (undefined), confirming the fix doesn't regress outage safety.
  • New test: a MIX of confirmed-not-found and a transient error still fails open (can't rule out a real issue behind the failure).
  • New direct unit tests for fetchLinkedIssueFacts itself (test/unit/backfill.test.ts, no prior dedicated coverage existed): found-with-fallback-defaults (sparse payload missing number/state), confirmed not_found on 404, fetch_error on 5xx.
  • npx tsc --noEmit clean.
  • Scoped: linked-issue-hard-rules.test.ts — 28 passed; gate-check-policy.test.ts — 85 passed; backfill.test.ts — 143 passed.
  • Regression sweep: full queue.test.ts — 202 passed.
  • Diff-range coverage-gap check on all three changed source files: fully covered.
  • Full unsharded npm run test:coverage: 5605 passed, 4 skipped (pre-existing/unrelated), 0 failed.
  • npm audit --audit-level=moderate: 0 vulnerabilities.

Advances #1936. Closes #2136.

…e violation

fetchLinkedIssueFacts collapsed a confirmed 404 and a transient fetch
failure into the same undefined result. resolveLinkedIssueHardRule
then treated an all-undefined fetch batch as "no violation" (fail
open) regardless of WHY every fetch came back empty — so a contributor
citing a fabricated issue number (e.g. "Fixes #999999") satisfied the
"has a linked issue" check and was fully exempt from the
ownerAssignedClose/missingPointLabelClose/maintainerOnlyLabelClose
hard rules, even on a repo with all three set to block. The codebase
already closed this exact evasion class for reference overflow
(too-many-refs = a violation, not a silent skip); it was never applied
to the all-404 case.

fetchLinkedIssueFacts now returns a tri-state result (found /
not_found / fetch_error) instead of collapsing both into undefined.
resolveLinkedIssueHardRule treats an ALL-not_found fetch batch the
same way it already treats overflow: an unsafe-to-verify violation.
A single fetch_error in the mix still fails open, since a genuine
outage could be masking a real, rule-violating issue.
@dosubot dosubot Bot added the size:M label Jul 1, 2026
@loopover-orb

loopover-orb Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-02 00:13:14 UTC

5 files · 1 AI reviewer · no blockers · readiness 75/100 · CI green · clean

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review

Review summary
The change correctly separates confirmed 404s from transient linked-issue fetch failures and wires the tri-state result through the hard-rule evaluator and author-login helper. The hard-rule behavior is appropriately fail-closed only for an all-confirmed-not-found batch while preserving fail-open behavior for public-token/no-token 404s and mixed transient failures. The visible tests exercise the important routing cases, including the public-token fallback and mixed not-found/fetch-error path.

Nits — 6 non-blocking
  • nit: src/github/backfill.ts:2897 declares `let result` without an explicit type, which makes the transformation block less self-documenting than the old directly-typed await expression.
  • nit: src/github/backfill.ts:2898 documents malformed bodies as `fetch_error`, but only the `githubJsonWithHeaders` await is inside the try/catch; keep the post-fetch shape extraction inside the guarded section as defensive hardening if malformed JSON can still reach `result.data`.
  • src/github/backfill.ts:2897 can keep the strong generic shape by typing `result` explicitly or moving the transformation into the try block so the new tri-state contract is easier to audit.
  • test/unit/backfill.test.ts:4847 should include a malformed/empty payload case if you intend `fetch_error` to cover malformed bodies, matching the new contract comment.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #999999, #2136
Related work ⚠️ 2 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (size label size:M; 2 linked issues).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 65 registered-repo PR(s), 55 merged, 572 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 65 PR(s), 572 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Review context
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Review top overlaps.
  • Add a concise scope and risk note.
  • Triage stale or unlinked PRs.
  • No action.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 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.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added gittensor gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. labels Jul 1, 2026
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.73%. Comparing base (17d70b2) to head (24c4a64).
⚠️ Report is 46 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2402      +/-   ##
==========================================
+ Coverage   95.71%   95.73%   +0.01%     
==========================================
  Files         222      222              
  Lines       24661    24667       +6     
  Branches     8949     8952       +3     
==========================================
+ Hits        23605    23614       +9     
+ Misses        433      430       -3     
  Partials      623      623              
Files with missing lines Coverage Δ
src/github/backfill.ts 96.36% <100.00%> (+0.21%) ⬆️
src/queue/processors.ts 90.27% <100.00%> (+0.05%) ⬆️
src/review/linked-issue-hard-rules.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…cated 404

GitHub returns 404 both for a genuinely nonexistent issue and for a
real-but-inaccessible one (private repo, no grant) -- it deliberately
doesn't distinguish the two. fetchLinkedIssueFacts treated every 404
as confirmed absence regardless of token, so a missing/under-scoped
installation token falling back to the public token could get a
false-positive "not_found" on a real linked issue, tripping the
linked-issue hard-rule close.

Only classify a 404 as not_found when the token used is a genuine,
non-public credential; otherwise treat it as fetch_error (fails open),
consistent with every other unprovable-outcome path in this function.
@JSONbored
JSONbored merged commit 88513ec into main Jul 2, 2026
12 checks passed
@JSONbored
JSONbored deleted the claude/linked-issue-nonexistent-tristate branch July 2, 2026 00:19
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

fix(review): a nonexistent linked issue number satisfies the linked-issue hard rule

1 participant