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
2 changes: 1 addition & 1 deletion src/review/content-lane-wire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export function applySurfaceGate(
if (generic.blockers.length === 0 && generic.conclusion === "success") return surface; // generic was clean → surface stands
if (generic.blockers.length === 0) {
if (surface.conclusion === "success") return generic;
return surface;
return { ...surface, warnings: [...generic.warnings, ...surface.warnings] };
}
// #3907: opt-in escape hatch from guard #3 below. Default (null/undefined/"advisory") preserves today's
// behavior byte-identically. "gate" skips the override entirely, so an AI-judgment-only failure falls
Expand Down
39 changes: 32 additions & 7 deletions test/unit/content-lane-wire.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,39 @@ describe("applySurfaceGate", () => {

expect(applySurfaceGate(genericHold, surfaceMerge)).toBe(genericHold);
});
it("lets a surface hard failure override a generic warning-only hold", () => {
const genericHold = gate({
conclusion: "neutral",
blockers: [],
warnings: [{ code: "oversized_pr", title: "Large change", severity: "warning", detail: "large" }],
});
it("lets a surface hard failure override a generic warning-only hold, preserving the generic gate's hold warnings", () => {
const oversizedPrFinding: AdvisoryFinding = { code: "oversized_pr", title: "Large change", severity: "warning", detail: "large" };
const genericHold = gate({ conclusion: "neutral", blockers: [], warnings: [oversizedPrFinding] });

const out = applySurfaceGate(genericHold, surfaceClose);
expect(out?.conclusion).toBe("failure");
expect(out?.blockers).toEqual(surfaceClose.blockers);
expect(out?.warnings).toEqual([oversizedPrFinding]);
});
it("the unchanged sub-case: a success generic with warnings still returns the surface verbatim", () => {
const genericSuccess = gate({ conclusion: "success", blockers: [], warnings: [{ code: "quality_readiness_low", title: "Readiness is low", severity: "warning", detail: "" }] });
expect(applySurfaceGate(genericSuccess, surfaceClose)).toBe(surfaceClose);
});
it("the unchanged sub-case: a holding generic against a success surface still returns the generic verbatim", () => {
const oversizedPrFinding: AdvisoryFinding = { code: "oversized_pr", title: "Large change", severity: "warning", detail: "large" };
const genericHold = gate({ conclusion: "neutral", blockers: [], warnings: [oversizedPrFinding] });
const surfaceMerge = gate({ conclusion: "success", title: "Surface", summary: "valid entry" });
expect(applySurfaceGate(genericHold, surfaceMerge)).toBe(genericHold);
});
it("REGRESSION (#10011): preserves the generic gate's hold warnings when a non-success surface verdict overrides it", () => {
const oversizedPrFinding: AdvisoryFinding = { code: "oversized_pr", title: "Large change", severity: "warning", detail: "large" };
const genericHold = gate({ conclusion: "neutral", blockers: [], warnings: [oversizedPrFinding] });

const manual = surfaceVerdictToGate({ verdict: "manual", summary: "auth declared" }).evaluation;
const outManual = applySurfaceGate(genericHold, manual);
expect(outManual?.conclusion).toBe("neutral");
expect(outManual?.warnings.map((w) => w.code)).toEqual(["oversized_pr", "surface_lane_manual"]);

expect(applySurfaceGate(genericHold, surfaceClose)).toBe(surfaceClose);
const close = surfaceVerdictToGate({ verdict: "close", summary: "bad entry" }).evaluation;
const outClose = applySurfaceGate(genericHold, close);
expect(outClose?.conclusion).toBe("failure");
expect(outClose?.blockers.map((b) => b.code)).toEqual(["surface_lane_reject"]);
expect(outClose?.warnings.map((w) => w.code)).toEqual(["oversized_pr"]);
});
it("PRESERVES a generic hard blocker over a surface merge (a committed secret can never merge)", () => {
const secret: AdvisoryFinding = { code: "secret_leak", title: "Secret", severity: "critical", detail: "leaked key" };
Expand Down