ump_spec.gates
module ump-spec
Gate evaluator: the POC stand-in for OPA/Rego (spec §15, §25.4).
evaluate(kind, transition, artifact_doc, context, mode) selects the gates in the catalogue
(registries/gates/*.yaml) that apply to that artifact kind and transition (or operation=
for non-transition hooks such as combine, calibrate, pool, approve, seal,
export, agent_session), runs each gate’s registered Python predicate, and returns
GateResult rows in the fixed apply order: hard stops, exclusions, fallbacks, routes, warnings,
then info/computed and passes.
Gates degrade before they block. Only the eight hard stops stop by default; mode="strict" (or a
gate listed in strict_gates) turns a Fall back outcome into strict_stop for gates whose
strict_override is true. Strict affects only Fall back: Exclude and continue (EVIDENCE_EXCLUDED
stays a note), Route, Warn and Computed outcomes are unchanged (spec §15). An unknown id in gate_ids
or strict_gates is SCHEMA_INVALID. raise_for_stops leads with hard stops, then strict stops,
each in gate catalogue order, and lists every stop in errors (spec §19). Gates without a POC predicate
pass with an NOT_IMPLEMENTED_IN_POC info note and predicate="not_implemented_in_poc".
Hard stops fail closed (spec §15): on an operation inside a hard stop’s binding (HARD_STOP_BINDINGS), an input
the gate needs that is missing, malformed or unverifiable is a failure, never not applicable.
Context keys read by predicates are listed in CONTEXT_KEYS; they are optional except where a hard stop’s
binding needs them.
Classes
Section titled “Classes”GateContext
Section titled “GateContext”class
class GateContextFields
| Name | Type | Default | Description |
|---|---|---|---|
gate | GateDefinition | ||
kind | str | None | ||
transition | str | None | ||
operation | str | None | ||
doc | dict[str, Any] | ||
ctx | dict[str, Any] | ||
mode | Mode |
Methods and properties (2)
threshold method
def threshold(self, name: str, default: Any = None) -> Anyinit constructor
GateContext( self, gate: GateDefinition, kind: str | None, transition: str | None, operation: str | None, doc: dict[str, Any], ctx: dict[str, Any], mode: Mode,) -> NoneSource: packages/ump-spec/src/ump_spec/gates.py, line 107
class
class CheckFields
| Name | Type | Default | Description |
|---|---|---|---|
passed | bool | ||
message | str | '' | |
applicable | bool | True | |
outcome | GateOutcome | None | None | |
fallbacks | list[FallbackEntry] | field(default_factory=list) | |
notes | list[Note] | field(default_factory=list) | |
evidence | dict[str, Any] | field(default_factory=dict) | |
route_to | str | None | None |
Methods and properties (1)
init constructor
Check( self, passed: bool, message: str = '', applicable: bool = True, outcome: GateOutcome | None = None, fallbacks: list[FallbackEntry] = list(), notes: list[Note] = list(), evidence: dict[str, Any] = dict(), route_to: str | None = None,) -> NoneSource: packages/ump-spec/src/ump_spec/gates.py, line 124
GateSummary
Section titled “GateSummary”class
class GateSummaryFields
| Name | Type | Default | Description |
|---|---|---|---|
stopped | bool | ||
next_state | Literal['checks_passed', 'checks_failed'] | ||
stops | list[GateResult] | ||
exclusions | list[GateResult] | ||
fallbacks | list[GateResult] | ||
routes | list[GateResult] | ||
warnings | list[GateResult] | ||
fallback_entries | list[FallbackEntry] | ||
notes | list[Note] | ||
route_to | list[str] |
Methods and properties (2)
as_dict method
def as_dict(self) -> dict[str, Any]init constructor
GateSummary( self, stopped: bool, next_state: Literal['checks_passed', 'checks_failed'], stops: list[GateResult], exclusions: list[GateResult], fallbacks: list[GateResult], routes: list[GateResult], warnings: list[GateResult], fallback_entries: list[FallbackEntry], notes: list[Note], route_to: list[str],) -> NoneSource: packages/ump-spec/src/ump_spec/gates.py, line 1955
Functions
Section titled “Functions”predicate
Section titled “predicate”function
def predicate(*gate_ids: str) -> Callable[[Predicate], Predicate]Register a predicate for one or more gate ids.
Source: packages/ump-spec/src/ump_spec/gates.py, line 142
hard_stop_bound
Section titled “hard_stop_bound”function
def hard_stop_bound(gc: GateContext) -> boolTrue when gc is an operation inside its hard stop’s binding, so missing input must fail.
Source: packages/ump-spec/src/ump_spec/gates.py, line 415
differencing_risk
Section titled “differencing_risk”function
def differencing_risk( aggregate: dict[str, Any], released: list[dict[str, Any]], floor: int,) -> dict[str, Any] | NoneThe differencing risk of releasing aggregate after released (G-PRIV-03, spec §20), or None.
Inputs are {query, n, cells?: [{id, n}]} and assumed well formed (the gate checks shape first). Three rules,
the first that finds a group below floor wins:
same_query: a repeat of a released query (rules.normalise_query) whose total differs by 0 < d < floor;cell_resized: a cell released before whose size changed by 0 < d < floor;set_overlap: a group of cells that the new aggregate, with any set of released ones, isolates. Pairwise checks are necessary and not sufficient, so the search runs over the whole set.
Clean-room connectors call this, so the rule lives only here.
Source: packages/ump-spec/src/ump_spec/gates.py, line 722
differencing_words
Section titled “differencing_words”function
def differencing_words(risk: dict[str, Any]) -> strPlain words for a differencing_risk result, naming its rule and how many people it could single out.
A set_overlap found by the conservative check (exact: false) says so.
Source: packages/ump-spec/src/ump_spec/gates.py, line 759
find_personal_data
Section titled “find_personal_data”function
def find_personal_data(rec: Any, ptr: str = '') -> list[str]Pointers to fields that look like personal data (keys or email/phone-like strings).
Source: packages/ump-spec/src/ump_spec/gates.py, line 988
is_write_or_run_tool
Section titled “is_write_or_run_tool”function
def is_write_or_run_tool(name: str) -> boolSource: packages/ump-spec/src/ump_spec/gates.py, line 1031
select_gates
Section titled “select_gates”function
def select_gates( kind: str | None, transition: str | None = None, operation: str | None = None, catalogue: dict[str, GateDefinition] | None = None,) -> list[GateDefinition]Gates that apply to kind at transition (target state) and/or operation.
Source: packages/ump-spec/src/ump_spec/gates.py, line 1858
evaluate
Section titled “evaluate”function
def evaluate( kind: str | None, transition: str | None, artifact_doc: dict[str, Any] | None, context: dict[str, Any] | None = None, mode: Mode = 'fallback', *, operation: str | None = None, gate_ids: Iterable[str] | None = None, strict_gates: Iterable[str] = (), artifact_id: str | None = None, artifact_version: int | None = None,) -> list[GateResult]Run the applicable gates and return results in apply order (stops first).
Source: packages/ump-spec/src/ump_spec/gates.py, line 1886
summarize
Section titled “summarize”function
def summarize(results: list[GateResult]) -> GateSummarySource: packages/ump-spec/src/ump_spec/gates.py, line 1982
raise_for_stops
Section titled “raise_for_stops”function
def raise_for_stops(results: list[GateResult]) -> NoneRaise UmpError when anything stopped (spec §19). The top-level problem is the most urgent stop: hard
stops first, then strict stops, each in gate catalogue order. errors lists every stop. Hard stops keep
their registered status (403 for privacy, approver and tool separation; 409 for integrity); a strict stop
takes its code’s strict status (e.g. ESTIMAND_UNDECLARED 422), or 409 when the code has none.
Source: packages/ump-spec/src/ump_spec/gates.py, line 2011
implemented_gates
Section titled “implemented_gates”function
def implemented_gates() -> list[str]Source: packages/ump-spec/src/ump_spec/gates.py, line 2036
Constants
Section titled “Constants”| Name | Type | Value | Description |
|---|---|---|---|
Mode | GateFailMode | ||
CONTEXT_KEYS | dict[str, str] | {'today': 'date for use-by checks (default: today UTC)', 'source_type': 'sour... | |
NA | Check(passed=True, applicable=False, message='not applicable') | ||
Predicate | Callable[[GateContext], Check] | ||
PREDICATES | dict[str, Predicate] | {} | |
HARD_STOP_BINDINGS | dict[str, dict[str, frozenset[str]]] | {'G-PRIV-01': {'operations': _PRIV_BOUND, 'transitions': frozenset()}, 'G-PRI... | Operations (and target states) on which each hard stop fails closed (spec §15). materialize binds the G-PRIV gates only under copy_policy: aggregates_only (a clean-room read). |
OVERLAP_EXACT_MAX_GROUPS | 12 | Most small atoms (Venn atoms below the floor) the exact set-overlap search enumerates unions of (proposed). Past it the check is conservative but still only refuses when a small atom could be part of a newly isolated group (see _set_overlap). | |
OVERLAP_EXACT_MAX_WORK | 200000 | Most fraction updates the set-overlap basis may spend (proposed). Sparse histories (stars, chains, padded links) of 200 linked releases spend a few thousand; a dense history that would spend more falls back to the conservative region check, which refuses any small positive Venn atom of the linked part (exact: false). | |
WRITE_RUN_TOOL_RE | re.compile('^(draft_|run_|write_|create_|update_|delete_|approve|transition|s... |