You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two gate-configuration hazards that both fail toward merge, in a system whose other defaults are
carefully fail-closed.
1. mergeReadiness: advisory silently turns off three hard gates at once
applyMergeReadinessGate (src/rules/advisory.ts:1392-1401) is a loosening composite: setting mergeReadiness to advisory demotes linkedIssue, duplicatePr, and slop from block to advisory —
even where the operator set each of those to block explicitly.
The behaviour is documented, but it is the one knob in the config surface that can disable three
independent gates with a single value, and nothing in the effective-config surfaces
(loopover_get_gate_config_effective, the settings preview) flags that a per-rule block has been
overridden by a composite. An operator reading their manifest back sees linkedIssue: block and
reasonably concludes the gate is on.
2. Unknown or malformed gate modes coerce toward merge
resolveConfiguredGateMode returns "off" for an unrecognised finding code
(src/rules/advisory.ts:1187), and gateMode() coerces any invalid string to "advisory" (:1384).
Both directions fail open.
This is currently neutralised upstream — normalizeOptionalGateMode
(packages/loopover-engine/src/focus-manifest.ts:1591-1599) rejects a typo'd mode to null (no
override), and the API path is a zod enum (src/openapi/schemas.ts:773). So this is defence-in-depth, not
a live bug. It is worth closing anyway because the safety of the inner function currently depends
entirely on every future caller remembering to normalise first, and #9065 shows the manifest surface has
no unknown-key validation to lean on.
Impact
(1) is a live misconfiguration hazard on the surface most likely to be edited by a self-hoster.
(2) is latent but removes a load-bearing assumption from the one place gate strictness is decided.
Requirements
Make the merge-readiness composite's effect visible: when it demotes an explicitly-configured block, emit a config-lint warning and surface the override in the effective-config output, so the
resolved mode and the authored mode are never silently different.
Consider whether the composite should be able to override an explicit per-rule block at all, or only
fill in unset ones. The latter is the least-surprising semantic.
Make resolveConfiguredGateMode and gateMode() fail closed (unknown → block, or throw) so
safety does not depend on caller discipline. Verify no current caller relies on the permissive coercion
before changing it.
Problem
Two gate-configuration hazards that both fail toward merge, in a system whose other defaults are
carefully fail-closed.
1.
mergeReadiness: advisorysilently turns off three hard gates at onceapplyMergeReadinessGate(src/rules/advisory.ts:1392-1401) is a loosening composite: settingmergeReadinesstoadvisorydemoteslinkedIssue,duplicatePr, andslopfromblockto advisory —even where the operator set each of those to
blockexplicitly.The behaviour is documented, but it is the one knob in the config surface that can disable three
independent gates with a single value, and nothing in the effective-config surfaces
(
loopover_get_gate_config_effective, the settings preview) flags that a per-ruleblockhas beenoverridden by a composite. An operator reading their manifest back sees
linkedIssue: blockandreasonably concludes the gate is on.
2. Unknown or malformed gate modes coerce toward merge
resolveConfiguredGateModereturns"off"for an unrecognised finding code(
src/rules/advisory.ts:1187), andgateMode()coerces any invalid string to"advisory"(:1384).Both directions fail open.
This is currently neutralised upstream —
normalizeOptionalGateMode(
packages/loopover-engine/src/focus-manifest.ts:1591-1599) rejects a typo'd mode tonull(nooverride), and the API path is a zod enum (
src/openapi/schemas.ts:773). So this is defence-in-depth, nota live bug. It is worth closing anyway because the safety of the inner function currently depends
entirely on every future caller remembering to normalise first, and #9065 shows the manifest surface has
no unknown-key validation to lean on.
Impact
(1) is a live misconfiguration hazard on the surface most likely to be edited by a self-hoster.
(2) is latent but removes a load-bearing assumption from the one place gate strictness is decided.
Requirements
block, emit a config-lint warning and surface the override in the effective-config output, so theresolved mode and the authored mode are never silently different.
blockat all, or onlyfill in unset ones. The latter is the least-surprising semantic.
resolveConfiguredGateModeandgateMode()fail closed (unknown →block, or throw) sosafety does not depend on caller discipline. Verify no current caller relies on the permissive coercion
before changing it.
config-lint.ts's checks alongside orb(config): no unknown-key validation at runtime — a typo silently disables a safety control with zero warning #9065's unknown-key work.Test Coverage Requirements
99%+ patch coverage, branch-counted; both arms of the composite demotion (explicit vs unset per-rule
mode), and the unknown-code path.
Links & Resources
src/rules/advisory.ts~1187, ~1384, ~1392-1401;packages/loopover-engine/src/focus-manifest.ts~1591-1599;src/openapi/schemas.ts~773;src/selfhost/config-lint.tsmaintainer-only — gate configuration safety.