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: 15 additions & 1 deletion src/queue/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3883,7 +3883,7 @@ export async function maybeAddSecretLeakFinding(
/* v8 ignore next -- fail-safe: a file-load error never destabilizes the gate. */
console.error(
JSON.stringify({
level: "warn",
level: "error",
event: "secret_scan_failed",
repository: args.repoFullName,
pullNumber: args.pullNumber,
Expand Down Expand Up @@ -4070,6 +4070,9 @@ async function auditGateCheckPermissionMissing(
detail: warning,
metadata: { deliveryId, repoFullName },
});
// Surface the install-wide Checks:write gap to Sentry — until the scope is granted the required gate check-run
// silently never posts on ANY PR for this install; an operator must SEE this config fault, not just the ledger.
console.error(JSON.stringify({ level: "error", event: "gate_check_permission_missing", repository: repoFullName, pullNumber, deliveryId }));
}

/**
Expand Down Expand Up @@ -4899,6 +4902,7 @@ async function maybePublishPrPublicSurface(
detail: checkRunResult.warning,
metadata: { deliveryId: webhook.deliveryId, repoFullName },
});
console.error(JSON.stringify({ level: "error", event: "check_run_permission_missing", repository: repoFullName, pullNumber: pr.number, deliveryId: webhook.deliveryId }));
} else if (checkRunResult?.kind === "published") {
publishedOutputs.push("check_run");
}
Expand Down Expand Up @@ -5305,6 +5309,16 @@ async function maybePublishPrPublicSurface(
failedOutputs,
},
});
// The advisory ran but NOTHING reached the PR (revoked token / perms removed / GitHub 5xx). For an
// advisory-only bot this is the worst failure — escalate to Sentry at error level, not just the audit ledger.
captureReviewFailure(new Error("PR public-surface publish failed — review produced output but nothing was posted to the PR"), {
kind: "publish",
owner: repoFullName.split("/")[0],
repo: repoFullName,
pr: pr.number,
head_sha: advisory.headSha,
failedOutputs: failedOutputs.map((failure) => failure.output),
});
}
return gateEvaluation;
}
Expand Down
5 changes: 5 additions & 0 deletions test/unit/queue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { generateKeyPairSync } from "node:crypto";
import { clearInstallationTokenCacheForTest } from "../../src/github/app";
import * as repositoriesModule from "../../src/db/repositories";
import * as sentryModule from "../../src/selfhost/sentry";
import {
listCollisionEdges,
createAgentRun,
Expand Down Expand Up @@ -4673,6 +4674,7 @@ describe("queue processors", () => {
if (url.includes("/commits/context500/check-runs")) return new Response("GitHub check API failed", { status: 500 });
return new Response("not found", { status: 404 });
});
const captureSpy = vi.spyOn(sentryModule, "captureReviewFailure");

await expect(
processJob(env, {
Expand All @@ -4698,6 +4700,9 @@ describe("queue processors", () => {
.first<{ detail: string; metadata_json: string }>();
expect(aggregate).toMatchObject({ detail: "check_run" });
expect(aggregate?.metadata_json).toContain('"output":"check_run"');
// The total publish failure (nothing reached the PR) escalates to Sentry at error level, not just the ledger.
expect(captureSpy).toHaveBeenCalledWith(expect.any(Error), expect.objectContaining({ kind: "publish", repo: "JSONbored/gittensory" }));
captureSpy.mockRestore();
});

it("audits disabled public-surface skips without miner lookup", async () => {
Expand Down
Loading