Skip to content
Merged
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
16 changes: 14 additions & 2 deletions src/rules/advisory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
};
}

Expand Down
43 changes: 43 additions & 0 deletions test/unit/rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading