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
8 changes: 8 additions & 0 deletions packages/loopover-engine/src/review/guardrail-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ export const ENGINE_DECISION_GUARDRAIL_GLOBS = [
"src/review/cutover-gate.ts",
"src/review/linked-issue-hard-rules.ts",
"src/review/outcomes-wire.ts",
// #8698: the guardrail-matching engine and the gate-decision core themselves were a self-referential blind
// spot -- none of the packages/loopover-engine files that actually decide a PR's disposition were listed, so
// a PR weakening the glob matcher, flipping a block/advisory disposition, or rewiring the orchestrator would
// not itself trip the hard guardrail. Listed on the engine side (no src/ shim exists for these) for the same
// reason the autonomy/guardrail-config real paths are: it is the substantive logic a PR edit would change.
"packages/loopover-engine/src/signals/change-guardrail.ts",
"packages/loopover-engine/src/advisory/gate-advisory.ts",
"packages/loopover-engine/src/predicted-gate.ts",
];

// Default, safe-by-default invariant set (restored by #3943 after the original pure-config-as-code design
Expand Down
25 changes: 25 additions & 0 deletions test/unit/guardrail-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,31 @@ describe("ENGINE_DECISION_GUARDRAIL_GLOBS — post-#6203-migration real paths (#
});
});

// #8698: the guardrail-matching engine itself (change-guardrail.ts), the gate-decision advisory core
// (gate-advisory.ts), and the predicted-gate orchestrator were never listed -- a self-referential blind spot,
// since a PR weakening exactly those files would not itself trip the hard guardrail. Each must now be a hit.
describe("ENGINE_DECISION_GUARDRAIL_GLOBS — guardrail engine + gate-advisory/predicted-gate self-protection (#8698)", () => {
it("lists the guardrail-matching engine, the gate-advisory core, and the predicted-gate orchestrator", () => {
expect(ENGINE_DECISION_GUARDRAIL_GLOBS).toContain("packages/loopover-engine/src/signals/change-guardrail.ts");
expect(ENGINE_DECISION_GUARDRAIL_GLOBS).toContain("packages/loopover-engine/src/advisory/gate-advisory.ts");
expect(ENGINE_DECISION_GUARDRAIL_GLOBS).toContain("packages/loopover-engine/src/predicted-gate.ts");
});

it("a PR touching only the guardrail-matching engine trips the hard guardrail", () => {
expect(isGuardrailHit(["packages/loopover-engine/src/signals/change-guardrail.ts"], resolveHardGuardrailGlobs(null))).toBe(
true,
);
});

it("a PR touching only the gate-advisory core trips the hard guardrail", () => {
expect(isGuardrailHit(["packages/loopover-engine/src/advisory/gate-advisory.ts"], resolveHardGuardrailGlobs(null))).toBe(true);
});

it("a PR touching only the predicted-gate orchestrator trips the hard guardrail", () => {
expect(isGuardrailHit(["packages/loopover-engine/src/predicted-gate.ts"], resolveHardGuardrailGlobs(null))).toBe(true);
});
});

describe("resolveHardGuardrailGlobs", () => {
it("uses invariant guardrails when effective settings omit hardGuardrailGlobs", () => {
expect(resolveHardGuardrailGlobs(undefined)).toEqual(DEFAULT_HARD_GUARDRAIL_GLOBS);
Expand Down