Skip to content

orb(config): mergeReadiness:advisory silently demotes three explicitly-blocking gates at once, and unknown gate modes coerce toward merge #9167

Description

@JSONbored

Problem

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 upstreamnormalizeOptionalGateMode
(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.
  • Add the composite to 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

maintainer-only — gate configuration safety.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions