Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0032

ADR-0032: The gate adopts the admission-control contract

field value
Status Accepted
Date 2026-07-24
Register factory (docs/adr, 4-digit ids)
Source docs/adr/0032-gate-adopts-the-admission-control-contract.md — permalink at bdafa201a (unchanged on master since that commit)
Wiki register entry ADR-0032 (wiki status: Accepted)

← ADR-0031 · ADR-0033 →

The record

Context

ADR-0002 requires factory gates to fail closed. The shipped hook does the opposite, and the code says so out loud. In scripts/claude_governance_gate.py:

The fail-open choice is not indefensible on its own terms: a gate that wedges the tool it governs gets disabled, and a disabled gate governs nothing. But it is currently an undocumented, uniform choice — every failure mode gets the same permissive treatment, with no distinction between "the policy engine says allow" and "we could not determine what the policy engine thinks."

Kubernetes admission control has already worked out this contract in production, and every element of it addresses a gap we currently have:

Decision

Adopt the admission-control contract explicitly:

  1. Ordered stages: mutate, then validate. MODIFY is wired — the mutated input is what the validating stage sees and what executes. A MODIFY result may never be silently dropped; failing to apply a computed mutation is itself a gate failure.
  2. Mutators are idempotent. Applying a mutation twice yields the same result as applying it once.
  3. Every gate declares an explicit failure policy — fail-closed or fail-open — as data, not as an exception handler. The default is fail-closed (ADR-0002). A gate that declares fail-open must state why, and that declaration is reviewable.
  4. Undetermined ≠ allowed. Malformed input, engine error and timeout are distinct outcomes from ALLOW, are recorded as such, and are resolved by the declared failure policy — never by falling through to return 0.
  5. Self-exclusion is explicit. The set of operations required to diagnose or repair the governance path is declared and excluded, so a gate failure cannot make itself unrecoverable. This is what makes fail-closed survivable, and it is the missing precondition that made fail-open look necessary.
  6. Every gate declares a timeout, and timeout resolves through the failure policy.

Alternatives rejected

Consequences

Provenance

2026-07-24 prior-art research (Kubernetes admission control: mutate-then-validate, idempotent mutators, failurePolicy, self-exclusion, timeouts), matched against a read of scripts/claude_governance_gate.py and core/adapters/claude/tool_interceptor.py, which showed the uniform fail-open and the dropped MODIFY branch.

Relations of ADR-0032

Rulings cited

None.

Specs cited