Skip to main content

ScopedOwnerGateManager

scripts/goal-runner/scoped_owner_gate.py defines the ScopedOwnerGateManager. This is the only path for creating, listing, approving, and consuming owner gates.

Construction

from scoped_owner_gate import ScopedOwnerGateManager

manager = ScopedOwnerGateManager(store)

Public API

manager.list_open(scope: str | None = None) -> list[dict]
manager.approve(gate_id, *, approver, source, note, evidence_path=None) -> dict
manager.reject(gate_id, *, approver, reason, evidence_path=None) -> dict
manager.consume(gate_id, *, run_id, subject_sha) -> dict
manager.reserve(gate_id, *, run_id) -> dict

Validation rules

_validate enforces:

  • scope ∈ VALID_SCOPES
  • source ∈ EXTERNAL_HUMAN_SOURCES
  • source ∈ APPROVAL_SCOPE_REQUIRES[scope]
  • approver is not an agent-role identity
  • MERGE_APPROVAL does not accept owner-cli
  • PRODUCTION_PROMOTION does not accept owner-cli
  • subject_sha matches the runtime HEAD (in consume)

Subject binding

The consume call requires a subject_sha. If the runtime HEAD differs from gate.payload.subject_sha, consumption is refused.

Replay protection

After consume, the gate's dispatches_used is incremented. Subsequent consume calls on the same gate fail closed.

What the manager is NOT

  • It does not store private keys.
  • It does not verify signatures (that is signed_ticket_verifier.py).
  • It does not authorize destructive operations itself (it only persists the gate).

The manager is the gate state machine, not the operation executor. The orchestrator consumes the gate and then performs the operation.