diff --git a/packages/loopover-engine/src/review/guardrail-config.ts b/packages/loopover-engine/src/review/guardrail-config.ts index 02614de495..1277463b6d 100644 --- a/packages/loopover-engine/src/review/guardrail-config.ts +++ b/packages/loopover-engine/src/review/guardrail-config.ts @@ -27,7 +27,12 @@ export const ENGINE_DECISION_GUARDRAIL_GLOBS = [ "src/settings/agent-actions.ts", "src/settings/agent-execution.ts", "src/settings/agent-sweep.ts", + // #8012: src/settings/autonomy.ts is now a 5-line re-export shim (#4879's #6203-era migration) -- the real, + // substantive autonomy deny-by-default logic lives at the packages/loopover-engine path below. Both are kept: + // the shim is still a real (if thin) file, and listing the real path is what actually protects the logic a + // PR edit could otherwise change without ever touching the shim. "src/settings/autonomy.ts", + "packages/loopover-engine/src/settings/autonomy.ts", "src/queue/**", "src/github/pr-actions.ts", "src/github/app.ts", @@ -38,7 +43,11 @@ export const ENGINE_DECISION_GUARDRAIL_GLOBS = [ "src/scoring/**", "src/auth/**", "src/review/safety.ts", + // #8012: same shim/real split as autonomy.ts above -- src/review/guardrail-config.ts (this file's own + // pre-migration twin) is a re-export shim; the real list edited here lives at the packages/loopover-engine + // path below, so a PR silently narrowing DEFAULT_HARD_GUARDRAIL_GLOBS itself must trip this same guardrail. "src/review/guardrail-config.ts", + "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..febe8a3b6c 100644 --- a/test/unit/guardrail-config.test.ts +++ b/test/unit/guardrail-config.test.ts @@ -2,8 +2,10 @@ import { describe, expect, it } from "vitest"; import { CONFIG_AS_CODE_GUARDRAIL_GLOBS, DEFAULT_HARD_GUARDRAIL_GLOBS, + ENGINE_DECISION_GUARDRAIL_GLOBS, resolveHardGuardrailGlobs, } from "../../src/review/guardrail-config"; +import { isGuardrailHit } from "../../src/signals/change-guardrail"; describe("CONFIG_AS_CODE_GUARDRAIL_GLOBS", () => { it("guards the .loopover.* config files", () => { @@ -14,6 +16,27 @@ describe("CONFIG_AS_CODE_GUARDRAIL_GLOBS", () => { }); }); +// #8012: src/settings/autonomy.ts and src/review/guardrail-config.ts (the #6203-era migration's pre-migration +// paths, still listed above for their own sake) are now 5-line re-export shims -- the real, substantive logic +// a contributor PR could edit lives at the packages/loopover-engine paths below. A PR touching only the real +// path, never the shim, must still trip the guardrail. +describe("ENGINE_DECISION_GUARDRAIL_GLOBS — post-#6203-migration real paths (#8012)", () => { + it("lists both the real autonomy.ts and guardrail-config.ts engine-package paths, alongside their src/ shims", () => { + expect(ENGINE_DECISION_GUARDRAIL_GLOBS).toContain("packages/loopover-engine/src/settings/autonomy.ts"); + expect(ENGINE_DECISION_GUARDRAIL_GLOBS).toContain("src/settings/autonomy.ts"); + expect(ENGINE_DECISION_GUARDRAIL_GLOBS).toContain("packages/loopover-engine/src/review/guardrail-config.ts"); + expect(ENGINE_DECISION_GUARDRAIL_GLOBS).toContain("src/review/guardrail-config.ts"); + }); + + it("a PR touching only the real autonomy.ts path (never its shim) still trips the hard guardrail", () => { + expect(isGuardrailHit(["packages/loopover-engine/src/settings/autonomy.ts"], DEFAULT_HARD_GUARDRAIL_GLOBS)).toBe(true); + }); + + it("a PR touching only the real guardrail-config.ts path (never its shim) still trips the hard guardrail", () => { + expect(isGuardrailHit(["packages/loopover-engine/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);