From d612279d0c1e3b42e9be2b370f79c361e0415bf0 Mon Sep 17 00:00:00 2001 From: Jeff <158072326+jeffrey701@users.noreply.github.com> Date: Fri, 24 Jul 2026 03:58:24 -0700 Subject: [PATCH] fix(rules): disclose configured blockers omitted past the inline gate-check cap (#8323) --- src/rules/advisory.ts | 16 +++++++++++++-- test/unit/rules.test.ts | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/rules/advisory.ts b/src/rules/advisory.ts index 0ec5406aa8..5aa9023f56 100644 --- a/src/rules/advisory.ts +++ b/src/rules/advisory.ts @@ -387,6 +387,11 @@ function sanitizeForCheckRun(text: string): string { export const CHECK_RUN_ANNOTATION_LIMIT = 50; +// Max configured hard-blocker lines rendered inline in the gate check-run text before truncation — a long +// list is unreadable and risks GitHub's check-run output size limit. Overflow is disclosed via a +// "…N more omitted" line (formatGateCheckOutput, #8323), the same transparency CHECK_RUN_ANNOTATION_LIMIT gets. +const GATE_CHECK_BLOCKER_LIMIT = 8; + export type CheckRunAnnotation = { path: string; start_line: number; @@ -741,17 +746,24 @@ export function formatGateCheckOutput(gate: GateCheckEvaluation): { title: strin text: "LoopOver did not create a contributor-facing failure for this event.", }; } - const blockerLines = gate.blockers.slice(0, 8).map((finding) => { + const blockerLines = gate.blockers.slice(0, GATE_CHECK_BLOCKER_LIMIT).map((finding) => { const action = finding.action ? ` Action: ${sanitizeForCheckRun(finding.action)}` : ""; return `- ${sanitizeForCheckRun(finding.title)}.${action}`; }); + // #8323: disclose truncation, mirroring buildCheckRunAnnotations' omittedCount line — a contributor with 9+ + // genuine blockers must know more exist than are shown, not silently discover them on the next gate run. + const omittedCount = Math.max(0, gate.blockers.length - GATE_CHECK_BLOCKER_LIMIT); + let text = blockerLines.length > 0 ? blockerLines.join("\n") : "A configured hard blocker was found."; + if (omittedCount > 0) { + text = `${text}\n\n…${omittedCount} more configured blocker(s) omitted from inline check output.`; + } return { // GitHub's check-run output.title 422s when too long; cap it (matches the 255 cap used for annotations). // An unbounded title (e.g. when failing-check names are appended) threw a 422 that aborted the ENTIRE // review before the comment, audit, and auto-action — so red-CI PRs were never reviewed or closed. title: gate.title.slice(0, 255), summary: `${LOOPOVER_GATE_CHECK_NAME} found a repo-configured hard blocker.`, - text: blockerLines.length > 0 ? blockerLines.join("\n") : "A configured hard blocker was found.", + text, }; } diff --git a/test/unit/rules.test.ts b/test/unit/rules.test.ts index c12331e5ea..03a4042c1c 100644 --- a/test/unit/rules.test.ts +++ b/test/unit/rules.test.ts @@ -696,6 +696,49 @@ describe("advisory rules", () => { expect(output.text).not.toMatch(/reward|wallet|hotkey|trust score|payout|farming/i); }); + it("discloses how many configured blockers were omitted past the inline cap (#8323)", () => { + const blockers = Array.from({ length: 11 }, (_, i) => ({ + code: `configured_blocker_${i}`, + title: `Configured blocker ${i}`, + severity: "warning" as const, + detail: `detail ${i}`, + })); + const output = formatGateCheckOutput({ + enabled: true, + conclusion: "failure", + title: "LoopOver Orb Review Agent is blocking merge", + summary: "A configured merge-blocking issue was found.", + blockers, + warnings: [], + }); + + // Only the first 8 blocker lines are inlined, and the overflow (11 - 8 = 3) is disclosed, not silently dropped. + expect(output.text.match(/^- Configured blocker/gm)).toHaveLength(8); + expect(output.text).toContain("…3 more configured blocker(s) omitted from inline check output."); + }); + + it("does not add an omission-disclosure line when blockers are at or under the inline cap (#8323)", () => { + for (const count of [1, 8]) { + const blockers = Array.from({ length: count }, (_, i) => ({ + code: `configured_blocker_${i}`, + title: `Configured blocker ${i}`, + severity: "warning" as const, + detail: `detail ${i}`, + })); + const output = formatGateCheckOutput({ + enabled: true, + conclusion: "failure", + title: "LoopOver Orb Review Agent is blocking merge", + summary: "A configured merge-blocking issue was found.", + blockers, + warnings: [], + }); + + expect(output.text.match(/^- Configured blocker/gm), `count ${count}`).toHaveLength(count); + expect(output.text, `count ${count}`).not.toContain("omitted from inline check output"); + } + }); + it("keeps private reviewability context out of check output", () => { const pr: PullRequestRecord = { repoFullName: repo.fullName,