Context
src/review/linked-issue-hard-rules.ts's resolveLinkedIssueHardRule (lines ~190-231) orchestrates the linked-issue hard-rule decision. Line 205 computes:
if (extractLinkedIssueNumbersWithOverflow(args.body ?? "", args.repoFullName).overflow) { ... }
— a fresh, authoritative re-parse of the PR's current body, used only for its .overflow boolean (the "too many linked issues to verify safely" check). The freshly-parsed .numbers result is discarded. The function then runs its real per-issue fact-fetch loop (line 214, args.linkedIssues.map(...)) against a separate, caller-supplied args.linkedIssues array instead.
The one production call site (src/queue/processors.ts, ~line 2985-2995) passes body: pr.body, linkedIssues: pr.linkedIssues — both read off the same pr DB row, but pr.linkedIssues is itself a field populated by an earlier parse of the body at some prior sync point, not necessarily a live parse of the exact pr.body value fed into extractLinkedIssueNumbersWithOverflow moments earlier in the same function call. If a contributor edits their PR description to add a new linked-issue reference between the last pr.linkedIssues sync and this evaluation, the overflow check (reading the fresh body) may still pass, but the fact-fetch loop — which never sees the newly-added reference because it trusts the stale args.linkedIssues — silently never evaluates that issue against the configured hard rules until a later pass re-syncs pr.linkedIssues. This is a genuine detection-bypass window: the module's whole purpose is "evaluate every currently-linked issue's facts against the hard rules," and a body edit can currently outrun that evaluation.
Requirements
resolveLinkedIssueHardRule must derive the set of issue numbers it fetches facts for from the same extractLinkedIssueNumbersWithOverflow(args.body ?? "", args.repoFullName) call already made for the overflow check (using its .numbers result), rather than trusting the separately-supplied args.linkedIssues parameter — so the overflow check and the fact-fetch loop can never disagree about which issues are "currently linked."
- Preserve the existing
args.linkedIssues.length === 0 → return undefined early-out semantics (line 211) — source the emptiness check from the same fresh parse instead of the separate parameter.
- Either remove the now-redundant
linkedIssues parameter from resolveLinkedIssueHardRule's signature entirely (updating the one call site in src/queue/processors.ts to stop passing it), or keep the parameter for call-site compatibility but stop using it for the fact-fetch loop, deriving from the fresh parse instead. Pick exactly one direction and state which was chosen in the PR description.
- Every existing test covering
resolveLinkedIssueHardRule must keep passing.
Deliverables
Test Coverage Requirements
99%+ Codecov patch coverage, branch-counted, on every changed line/branch in linked-issue-hard-rules.ts and any call-site changes in src/queue/processors.ts, per codecov.yml.
Expected Outcome
The linked-issue hard-rule evaluator can no longer disagree with itself about which issues are currently linked to a PR — the overflow check and the per-issue fact evaluation are always computed from the identical, fresh body parse, closing the edit-driven detection-bypass window described above.
Links & Resources
Context
src/review/linked-issue-hard-rules.ts'sresolveLinkedIssueHardRule(lines ~190-231) orchestrates the linked-issue hard-rule decision. Line 205 computes:— a fresh, authoritative re-parse of the PR's current
body, used only for its.overflowboolean (the "too many linked issues to verify safely" check). The freshly-parsed.numbersresult is discarded. The function then runs its real per-issue fact-fetch loop (line 214,args.linkedIssues.map(...)) against a separate, caller-suppliedargs.linkedIssuesarray instead.The one production call site (
src/queue/processors.ts, ~line 2985-2995) passesbody: pr.body, linkedIssues: pr.linkedIssues— both read off the sameprDB row, butpr.linkedIssuesis itself a field populated by an earlier parse of the body at some prior sync point, not necessarily a live parse of the exactpr.bodyvalue fed intoextractLinkedIssueNumbersWithOverflowmoments earlier in the same function call. If a contributor edits their PR description to add a new linked-issue reference between the lastpr.linkedIssuessync and this evaluation, the overflow check (reading the fresh body) may still pass, but the fact-fetch loop — which never sees the newly-added reference because it trusts the staleargs.linkedIssues— silently never evaluates that issue against the configured hard rules until a later pass re-syncspr.linkedIssues. This is a genuine detection-bypass window: the module's whole purpose is "evaluate every currently-linked issue's facts against the hard rules," and a body edit can currently outrun that evaluation.Requirements
resolveLinkedIssueHardRulemust derive the set of issue numbers it fetches facts for from the sameextractLinkedIssueNumbersWithOverflow(args.body ?? "", args.repoFullName)call already made for the overflow check (using its.numbersresult), rather than trusting the separately-suppliedargs.linkedIssuesparameter — so the overflow check and the fact-fetch loop can never disagree about which issues are "currently linked."args.linkedIssues.length === 0 → return undefinedearly-out semantics (line 211) — source the emptiness check from the same fresh parse instead of the separate parameter.linkedIssuesparameter fromresolveLinkedIssueHardRule's signature entirely (updating the one call site insrc/queue/processors.tsto stop passing it), or keep the parameter for call-site compatibility but stop using it for the fact-fetch loop, deriving from the fresh parse instead. Pick exactly one direction and state which was chosen in the PR description.resolveLinkedIssueHardRulemust keep passing.Deliverables
resolveLinkedIssueHardRulesources its per-issue fetch list from the same body-derived parse used for the overflow check.src/queue/processors.ts's call site updated to match the new/simplified signature if the parameter was removed.bodywhose fresh parse yields an issue number that is NOT present in a separately-supplied stalelinkedIssuesarray, assert the hard-rule evaluation now still evaluates that issue (previously it would have silently been skipped).Test Coverage Requirements
99%+ Codecov patch coverage, branch-counted, on every changed line/branch in
linked-issue-hard-rules.tsand any call-site changes insrc/queue/processors.ts, percodecov.yml.Expected Outcome
The linked-issue hard-rule evaluator can no longer disagree with itself about which issues are currently linked to a PR — the overflow check and the per-issue fact evaluation are always computed from the identical, fresh body parse, closing the edit-driven detection-bypass window described above.
Links & Resources
src/review/linked-issue-hard-rules.ts(resolveLinkedIssueHardRule,extractLinkedIssueNumbersWithOverflow)src/queue/processors.ts(~line 2985, the sole call site)