Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0034
ADR-0034: Governed-action classification is property-derived, not name-enumerated
| field | value |
|---|---|
| Status | Proposed |
| Date | 2026-07-24 |
| Register | factory (docs/adr, 4-digit ids) |
| Source | docs/adr/0034-classification-is-property-derived-not-name-enumerated.md — permalink at bdafa201a (unchanged on master since that commit) |
| Wiki register entry | ADR-0034 (wiki status: Proposed) |
The record
- Status: Proposed
- Date: 2026-07-24
- Spec: SPEC-034 §2, §3
- Relates to: ADR-0008 (structure, not self-report), ADR-0011 (detect incompleteness)
Context
The constitution (constitutional.pl) classifies an action by looking its atom name up in
a flat, hand-maintained list per risk category:
security_threat(execute_untrusted). security_threat(bypass_auth).
involves_pii(access_pii). destructive_change(disable_governance).
Two failures are structural, not incidental:
- Synonym escape. The category is a set of names. A new operation that is genuinely a
security threat but reaches the gate under a different atom (
run_untrusted_code) is not in the list, so it passes every article. The list is a blocklist, and a blocklist's defining property is "an unlisted item is allowed." - The reduction is undefined. Nothing specifies how a real runtime operation becomes the
atom
access_piirather thanread_file. All the classification power — and all the risk — lives in that unwritten step, where an agent's self-description would decide the verdict.
This is the same class of hazard ADR-0008 closes for correctness (never trust the model's self-report) reappearing for classification.
Decision
An action's category membership is DERIVED from typed attributes of the action and the resources it targets, never from the action's atom name.
- An action is a structured descriptor:
action(Id, verb, targets, attributes...); each target resource carries typed attributes (env,class,reversibility,owner, ...). - Risk categories are defined by properties, not rosters:
security_threat(A) :- action_property(A, P), threat_property(P). - The category taxonomy (defining properties + subtype edges, e.g.
code_injectionis-auntrusted_executionis-asecurity_threat) is modelled in the canonical KG ontology (canonical_schema.py, SPEC-106), not as.plfacts — the constitution reasons over the taxonomy and stays a thin floor. - The article checks consume the derived category, so a new operation with a threat-bearing property is caught regardless of its name.
Alternatives rejected
- Keep enumerated name lists — the synonym-escape and undefined-reduction failures above; enforcement is only ever as complete as someone's memory of names.
- Let a model classify the action free-form at the gate — reintroduces the self-report ADR-0008 forbids; a model naming its own action is the reduction step handed to the untrusted party.
- A regex/keyword classifier over the operation text — brittle, and still not grounded in what the operation structurally is.
Consequences
- Requires the action/resource descriptor and the risk taxonomy to exist before the constitution can consume them — an ontology-first dependency (SPEC-106), consistent with the project's "design the ontology before importing" rule.
- Category membership becomes testable against properties (regress a rule against known-bearing and known-clear descriptors) rather than asserted by list membership.
- The blocklist hole does not vanish by itself — an action whose descriptor lacks a needed attribute is still unclassified; ADR-0011's incompleteness posture (below, and SPEC-034 §5) is what closes it.
Provenance
Mohamed, 2026-07-24: "how do we make these articles enforceable properly with proper lists and categorize actions properly?" — identifying that the enumerated name lists are the root fragility.
Related ADRs
Rulings cited
None.