diff --git a/packages/loopover-engine/src/review/guardrail-config.ts b/packages/loopover-engine/src/review/guardrail-config.ts index 02614de495..66c43cd125 100644 --- a/packages/loopover-engine/src/review/guardrail-config.ts +++ b/packages/loopover-engine/src/review/guardrail-config.ts @@ -28,6 +28,12 @@ export const ENGINE_DECISION_GUARDRAIL_GLOBS = [ "src/settings/agent-execution.ts", "src/settings/agent-sweep.ts", "src/settings/autonomy.ts", + // #8012: src/settings/autonomy.ts and src/review/guardrail-config.ts (below) are now 5-line #6203 re-export + // shims -- the real, substantive logic lives at these packages/loopover-engine paths instead. Both the shim + // AND the real path are listed deliberately: a PR touching only the shim (unlikely, but not impossible) + // should still trip the guardrail, and a PR touching the real engine-package file (the common case today) + // must trip it too, which the shim-only entries above could never catch on their own. + "packages/loopover-engine/src/settings/autonomy.ts", "src/queue/**", "src/github/pr-actions.ts", "src/github/app.ts", @@ -39,6 +45,10 @@ export const ENGINE_DECISION_GUARDRAIL_GLOBS = [ "src/auth/**", "src/review/safety.ts", "src/review/guardrail-config.ts", + // #8012: see the packages/loopover-engine/src/settings/autonomy.ts comment above -- same shim/real-path + // pairing, this time for this very file (guardrail-config.ts itself). A PR editing ENGINE_DECISION_GUARDRAIL_GLOBS + // or DEFAULT_HARD_GUARDRAIL_GLOBS to quietly remove entries must trip this guardrail too. + "packages/loopover-engine/src/review/guardrail-config.ts", "src/review/cutover-gate.ts", "src/review/linked-issue-hard-rules.ts", "src/review/outcomes-wire.ts", diff --git a/test/unit/guardrail-config.test.ts b/test/unit/guardrail-config.test.ts index ffc4b3865a..4d75f885ce 100644 --- a/test/unit/guardrail-config.test.ts +++ b/test/unit/guardrail-config.test.ts @@ -1,7 +1,9 @@ import { describe, expect, it } from "vitest"; +import { isGuardrailHit } from "../../src/signals/change-guardrail"; import { CONFIG_AS_CODE_GUARDRAIL_GLOBS, DEFAULT_HARD_GUARDRAIL_GLOBS, + ENGINE_DECISION_GUARDRAIL_GLOBS, resolveHardGuardrailGlobs, } from "../../src/review/guardrail-config"; @@ -14,6 +16,27 @@ describe("CONFIG_AS_CODE_GUARDRAIL_GLOBS", () => { }); }); +describe("ENGINE_DECISION_GUARDRAIL_GLOBS (#8012)", () => { + it("guards the real post-#6203 packages/loopover-engine paths, not just the pre-migration src/ shims", () => { + // The regression this guards against: a PR editing the autonomy deny-by-default dial, or editing + // ENGINE_DECISION_GUARDRAIL_GLOBS/DEFAULT_HARD_GUARDRAIL_GLOBS itself to quietly remove entries, touches + // only the real engine-package file and never the 5-line shim -- so it must still trip the guardrail. + const realPaths = [ + "packages/loopover-engine/src/settings/autonomy.ts", + "packages/loopover-engine/src/review/guardrail-config.ts", + ]; + for (const path of realPaths) { + expect(ENGINE_DECISION_GUARDRAIL_GLOBS).toContain(path); + expect(isGuardrailHit([path], DEFAULT_HARD_GUARDRAIL_GLOBS)).toBe(true); + } + }); + + it("still guards the pre-migration shim paths too (both are listed deliberately, neither replaces the other)", () => { + expect(isGuardrailHit(["src/settings/autonomy.ts"], DEFAULT_HARD_GUARDRAIL_GLOBS)).toBe(true); + expect(isGuardrailHit(["src/review/guardrail-config.ts"], DEFAULT_HARD_GUARDRAIL_GLOBS)).toBe(true); + }); +}); + describe("resolveHardGuardrailGlobs", () => { it("uses invariant guardrails when effective settings omit hardGuardrailGlobs", () => { expect(resolveHardGuardrailGlobs(undefined)).toEqual(DEFAULT_HARD_GUARDRAIL_GLOBS);