Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions coverage-8354/lcov.info
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
TN:
SF:src\review\linked-issue-hard-rules.ts
FN:34,loadLinkedIssueHardRules
FN:35,(anonymous_1)
FN:57,anyLinkedIssueHardRuleOn
FN:66,findMatchingLabel
FN:67,(anonymous_4)
FN:68,(anonymous_5)
FN:71,labelMatches
FN:75,issueIsAssignedToAuthor
FN:77,(anonymous_8)
FN:86,evaluateLinkedIssueHardRules
FN:105,(anonymous_10)
FN:171,mergeLinkedIssueHardRuleWithPersistedViolation
FN:195,resolveLinkedIssueHardRule
FN:220,(anonymous_13)
FN:221,(anonymous_14)
FN:227,(anonymous_15)
FN:258,hasVerifiableOpenLinkedIssueReference
FN:260,(anonymous_17)
FN:261,(anonymous_18)
FN:271,resolveLinkedIssueHasOpenReference
FN:284,(anonymous_20)
FN:287,(anonymous_21)
FNF:22
FNH:22
FNDA:4,loadLinkedIssueHardRules
FNDA:3,(anonymous_1)
FNDA:44,anyLinkedIssueHardRuleOn
FNDA:21,findMatchingLabel
FNDA:31,(anonymous_4)
FNDA:16,(anonymous_5)
FNDA:5,labelMatches
FNDA:23,issueIsAssignedToAuthor
FNDA:7,(anonymous_8)
FNDA:27,evaluateLinkedIssueHardRules
FNDA:6,(anonymous_10)
FNDA:9,mergeLinkedIssueHardRuleWithPersistedViolation
FNDA:12,resolveLinkedIssueHardRule
FNDA:13,(anonymous_13)
FNDA:13,(anonymous_14)
FNDA:7,(anonymous_15)
FNDA:17,hasVerifiableOpenLinkedIssueReference
FNDA:21,(anonymous_17)
FNDA:12,(anonymous_18)
FNDA:9,resolveLinkedIssueHasOpenReference
FNDA:1,(anonymous_20)
FNDA:57,(anonymous_21)
DA:28,1
DA:35,4
DA:50,1
DA:58,44
DA:67,31
DA:68,21
DA:72,5
DA:76,23
DA:77,23
DA:92,27
DA:93,27
DA:94,27
DA:96,22
DA:97,24
DA:98,23
DA:101,23
DA:105,6
DA:107,5
DA:114,18
DA:115,2
DA:122,16
DA:123,16
DA:124,4
DA:131,12
DA:132,1
DA:139,10
DA:176,9
DA:177,7
DA:178,5
DA:179,3
DA:208,12
DA:210,11
DA:211,12
DA:212,1
DA:217,10
DA:218,9
DA:219,12
DA:220,13
DA:221,13
DA:222,9
DA:227,7
DA:228,5
DA:229,1
DA:234,4
DA:236,4
DA:259,17
DA:260,21
DA:261,12
DA:277,9
DA:283,8
DA:284,7
DA:285,9
DA:286,9
DA:287,57
DA:288,7
LF:55
LH:55
BRDA:35,0,0,4
BRDA:35,0,1,3
BRDA:59,1,0,44
BRDA:59,1,1,24
BRDA:59,1,2,17
BRDA:59,1,3,11
BRDA:68,2,0,21
BRDA:68,2,1,12
BRDA:77,3,0,23
BRDA:77,3,1,6
BRDA:94,4,0,5
BRDA:94,4,1,22
BRDA:97,5,0,1
BRDA:97,5,1,23
BRDA:101,6,0,5
BRDA:101,6,1,18
BRDA:102,7,0,23
BRDA:102,7,1,10
BRDA:102,7,2,10
BRDA:102,7,3,9
BRDA:114,8,0,2
BRDA:114,8,1,16
BRDA:114,9,0,18
BRDA:114,9,1,5
BRDA:114,9,2,4
BRDA:123,10,0,4
BRDA:123,10,1,12
BRDA:123,11,0,16
BRDA:123,11,1,6
BRDA:123,11,2,5
BRDA:131,12,0,1
BRDA:131,12,1,11
BRDA:131,13,0,12
BRDA:131,13,1,6
BRDA:131,13,2,5
BRDA:134,14,0,1
BRDA:134,14,1,0
BRDA:176,15,0,2
BRDA:176,15,1,7
BRDA:177,16,0,2
BRDA:177,16,1,5
BRDA:178,17,0,2
BRDA:178,17,1,3
BRDA:179,18,0,3
BRDA:179,18,1,1
BRDA:208,19,0,1
BRDA:208,19,1,11
BRDA:210,20,0,11
BRDA:210,20,1,1
BRDA:211,21,0,1
BRDA:211,21,1,11
BRDA:217,22,0,1
BRDA:217,22,1,9
BRDA:218,23,0,9
BRDA:218,23,1,1
BRDA:221,24,0,4
BRDA:221,24,1,9
BRDA:222,25,0,5
BRDA:222,25,1,4
BRDA:228,26,0,1
BRDA:228,26,1,4
BRDA:259,27,0,1
BRDA:259,27,1,16
BRDA:260,28,0,7
BRDA:260,28,1,9
BRDA:260,29,0,21
BRDA:260,29,1,14
BRDA:277,30,0,1
BRDA:277,30,1,8
BRDA:283,31,0,1
BRDA:283,31,1,7
BRDA:284,32,0,1
BRDA:284,32,1,6
BRDA:285,33,0,9
BRDA:285,33,1,7
BRF:75
BRH:74
end_of_record
3 changes: 2 additions & 1 deletion src/queue/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2986,13 +2986,14 @@ async function runAgentMaintenancePlanAndExecute(
env,
repoFullName,
);
// Issue numbers are derived inside resolveLinkedIssueHardRule from a fresh body parse (#8354) — do not pass
// pr.linkedIssues here; that synced field can lag a body edit and disagree with the overflow check.
const liveLinkedIssueHardRule = await resolveLinkedIssueHardRule({
env,
repoFullName,
repoOwner,
config: linkedIssueRulesConfig,
body: pr.body,
linkedIssues: pr.linkedIssues,
ciToken,
prAuthorLogin: pr.authorLogin,
installationId,
Expand Down
14 changes: 10 additions & 4 deletions src/review/linked-issue-hard-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,18 @@ export function mergeLinkedIssueHardRuleWithPersistedViolation(
* - no rule in "block" mode → undefined (skip entirely, no fetch).
* - the PR body links MORE closing references than the cap (overflow) → a violation: too many to verify safely.
* - otherwise fetch each linked issue's facts (fail-open per issue) and run the deterministic evaluator.
*
* Both the overflow check and the per-issue fetch list come from ONE
* {@link extractLinkedIssueNumbersWithOverflow} call on `args.body` (#8354). Callers must not pass a separately
* synced `pr.linkedIssues` array — that field can lag a body edit and previously let a newly-added closing
* reference slip past hard-rule evaluation until a later sync.
*/
export async function resolveLinkedIssueHardRule(args: {
env: Env;
repoFullName: string;
repoOwner: string;
config: LinkedIssueHardRulesConfig;
body: string | null | undefined;
linkedIssues: number[];
ciToken: string | undefined;
prAuthorLogin?: string | null | undefined;
// The installation id for `ciToken` (undefined for public-token reads). The admission key is DERIVED from the
Expand All @@ -202,16 +206,18 @@ export async function resolveLinkedIssueHardRule(args: {
installationId?: number | null | undefined;
}): Promise<LinkedIssueHardRuleResult | undefined> {
if (!anyLinkedIssueHardRuleOn(args.config)) return undefined;
if (extractLinkedIssueNumbersWithOverflow(args.body ?? "", args.repoFullName).overflow) {
// Single fresh parse: overflow boolean AND the issue numbers to fetch must agree (#8354).
const linked = extractLinkedIssueNumbersWithOverflow(args.body ?? "", args.repoFullName);
if (linked.overflow) {
return {
violated: true,
reason: "PR body links more issues than LoopOver can safely verify automatically; please reduce linked closing references or request maintainer review.",
};
}
if (args.linkedIssues.length === 0) return undefined;
if (linked.numbers.length === 0) return undefined;
const token = args.ciToken ?? args.env.GITHUB_PUBLIC_TOKEN;
const admissionKey = githubRateLimitAdmissionKeyForToken(args.env, token, args.installationId);
const fetchResults = await Promise.all(args.linkedIssues.map((issueNumber) => fetchLinkedIssueFacts(args.env, args.repoFullName, issueNumber, token, admissionKey)));
const fetchResults = await Promise.all(linked.numbers.map((issueNumber) => fetchLinkedIssueFacts(args.env, args.repoFullName, issueNumber, token, admissionKey)));
const issueFacts = fetchResults.flatMap((result) => (result.status === "found" ? [result.facts] : []));
if (issueFacts.length === 0) {
// Every reference resolved to a CONFIRMED 404 — never a transient fetch_error (#2136). Mirrors the overflow
Expand Down
2 changes: 0 additions & 2 deletions src/services/agent-approval-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ export async function decidePendingAgentAction(env: Env, input: { id: string; de
repoOwner,
config: linkedIssueRulesConfig,
body: pr.body,
linkedIssues: pr.linkedIssues,
ciToken,
prAuthorLogin: pr.authorLogin,
installationId: pending.installationId,
Expand Down Expand Up @@ -377,7 +376,6 @@ export async function decidePendingAgentAction(env: Env, input: { id: string; de
repoOwner,
config: linkedIssueRulesConfig,
body: pr.body,
linkedIssues: pr.linkedIssues,
ciToken,
prAuthorLogin: pr.authorLogin,
installationId: pending.installationId,
Expand Down
Loading