⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.
Context
packages/loopover-engine/src/review/guardrail-config.ts:24-54's ENGINE_DECISION_GUARDRAIL_GLOBS
explicitly lists both the host-side shim and the real engine-package path for two file pairs (e.g.
src/settings/autonomy.ts + packages/loopover-engine/src/settings/autonomy.ts at lines 34-35;
src/review/guardrail-config.ts + its own engine path at lines 49-50), with comments explaining why
both are needed: a PR could otherwise weaken the real logic by only touching the engine-side file,
which wouldn't itself trip the hard-guardrail hold.
That same "list both sides" treatment was never extended to:
packages/loopover-engine/src/signals/change-guardrail.ts — the file that actually implements
isGuardrailHit (line 165) and guardrailPathMatches (line 139), i.e. the guardrail-matching
engine itself.
packages/loopover-engine/src/advisory/gate-advisory.ts — gate-decision core logic and the
check-run sanitizer (see the sibling issue filed alongside this one for a specific drift bug
there).
packages/loopover-engine/src/predicted-gate.ts — the orchestrator wiring both together.
src/rules/** (line 25) covers the host's src/rules/advisory.ts, but no
packages/loopover-engine/src/advisory/** or packages/loopover-engine/src/signals/** entry exists
anywhere in the guardrail glob list.
Consequence: resolveHardGuardrailGlobs/isGuardrailHit (via src/queue/processors.ts:2068 and
packages/loopover-engine/src/predicted-gate.ts:285-286,315) decides whether a PR is forced into
manual-review hold instead of being auto-mergeable by the one-shot gate. A contributor PR that edits
only packages/loopover-engine/src/signals/change-guardrail.ts (e.g. weakening the glob matcher to
always return false) or packages/loopover-engine/src/advisory/gate-advisory.ts (e.g. changing
block/advisory dispositions) would not itself trip the hard guardrail — a self-referential blind
spot in exactly the mechanism meant to catch this class of change.
Requirements
- Add
packages/loopover-engine/src/signals/change-guardrail.ts,
packages/loopover-engine/src/advisory/gate-advisory.ts, and
packages/loopover-engine/src/predicted-gate.ts to ENGINE_DECISION_GUARDRAIL_GLOBS, matching
the existing "list both host and engine path" pattern and its documented rationale.
Deliverables
All four Deliverables are required in the same PR.
Test Coverage Requirements
packages/loopover-engine/** is measured by codecov/patch (99%+ target, branch-counted). The
three new tests must exercise each previously-uncovered file path directly against
isGuardrailHit.
Expected Outcome
A PR touching the guardrail-matching engine itself, the gate-decision advisory core, or the
predicted-gate orchestrator correctly trips the hard guardrail hold, closing a self-referential
blind spot in the mechanism meant to catch exactly this class of change.
Links & Resources
packages/loopover-engine/src/review/guardrail-config.ts:24-54
(ENGINE_DECISION_GUARDRAIL_GLOBS, the list to extend)
packages/loopover-engine/src/signals/change-guardrail.ts:139,165 (isGuardrailHit,
guardrailPathMatches)
packages/loopover-engine/src/advisory/gate-advisory.ts, packages/loopover-engine/src/predicted-gate.ts:285-286,315
src/queue/processors.ts:2068 (a real call site consuming this guardrail decision)
- See the sibling issue filed alongside this one for a specific, currently-live drift bug in
gate-advisory.ts this same self-referential gap would have hidden from the guardrail
Context
packages/loopover-engine/src/review/guardrail-config.ts:24-54'sENGINE_DECISION_GUARDRAIL_GLOBSexplicitly lists both the host-side shim and the real engine-package path for two file pairs (e.g.
src/settings/autonomy.ts+packages/loopover-engine/src/settings/autonomy.tsat lines 34-35;src/review/guardrail-config.ts+ its own engine path at lines 49-50), with comments explaining whyboth are needed: a PR could otherwise weaken the real logic by only touching the engine-side file,
which wouldn't itself trip the hard-guardrail hold.
That same "list both sides" treatment was never extended to:
packages/loopover-engine/src/signals/change-guardrail.ts— the file that actually implementsisGuardrailHit(line 165) andguardrailPathMatches(line 139), i.e. the guardrail-matchingengine itself.
packages/loopover-engine/src/advisory/gate-advisory.ts— gate-decision core logic and thecheck-run sanitizer (see the sibling issue filed alongside this one for a specific drift bug
there).
packages/loopover-engine/src/predicted-gate.ts— the orchestrator wiring both together.src/rules/**(line 25) covers the host'ssrc/rules/advisory.ts, but nopackages/loopover-engine/src/advisory/**orpackages/loopover-engine/src/signals/**entry existsanywhere in the guardrail glob list.
Consequence:
resolveHardGuardrailGlobs/isGuardrailHit(viasrc/queue/processors.ts:2068andpackages/loopover-engine/src/predicted-gate.ts:285-286,315) decides whether a PR is forced intomanual-review hold instead of being auto-mergeable by the one-shot gate. A contributor PR that edits
only
packages/loopover-engine/src/signals/change-guardrail.ts(e.g. weakening the glob matcher toalways return
false) orpackages/loopover-engine/src/advisory/gate-advisory.ts(e.g. changingblock/advisory dispositions) would not itself trip the hard guardrail — a self-referential blind
spot in exactly the mechanism meant to catch this class of change.
Requirements
packages/loopover-engine/src/signals/change-guardrail.ts,packages/loopover-engine/src/advisory/gate-advisory.ts, andpackages/loopover-engine/src/predicted-gate.tstoENGINE_DECISION_GUARDRAIL_GLOBS, matchingthe existing "list both host and engine path" pattern and its documented rationale.
Deliverables
ENGINE_DECISION_GUARDRAIL_GLOBSincludes all three newly-identified engine-package files.isGuardrailHit(["packages/loopover-engine/src/signals/change-guardrail.ts"], resolveHardGuardrailGlobs(null))and asserts it now returnstrue— currentlyfalse.packages/loopover-engine/src/advisory/gate-advisory.ts.packages/loopover-engine/src/predicted-gate.ts.All four Deliverables are required in the same PR.
Test Coverage Requirements
packages/loopover-engine/**is measured bycodecov/patch(99%+ target, branch-counted). Thethree new tests must exercise each previously-uncovered file path directly against
isGuardrailHit.Expected Outcome
A PR touching the guardrail-matching engine itself, the gate-decision advisory core, or the
predicted-gate orchestrator correctly trips the hard guardrail hold, closing a self-referential
blind spot in the mechanism meant to catch exactly this class of change.
Links & Resources
packages/loopover-engine/src/review/guardrail-config.ts:24-54(
ENGINE_DECISION_GUARDRAIL_GLOBS, the list to extend)packages/loopover-engine/src/signals/change-guardrail.ts:139,165(isGuardrailHit,guardrailPathMatches)packages/loopover-engine/src/advisory/gate-advisory.ts,packages/loopover-engine/src/predicted-gate.ts:285-286,315src/queue/processors.ts:2068(a real call site consuming this guardrail decision)gate-advisory.tsthis same self-referential gap would have hidden from the guardrail