Orchestrata Ultimate / Specs / 034-governed-action-classification
034-governed-action-classification: Governed Action Classification
Replace the constitution's two structural weaknesses — enumerated risk-category name lists and a category-global consent flag — with (a) classification derived from typed properties of the action and the resources it targets, (b) authorization as a scoped grant matched to the specific action instance, from two sources (a human-ratified standing policy rule, or a per-instance human consent grant), and (c) a rule that every allow/deny decision — including the ones made in...
At a glance
| Item | Value |
|---|---|
| Spec directory | specs/034-governed-action-classification/ |
| Status line in the spec | Draft (design for review — NOT approved to build) |
| Requirement IDs defined | 22 |
With a @traces citation in src/ |
0 (0.0%) |
With a @traces citation in tests/ |
0 (0.0%) |
| Tracked files | 1 |
| RTM page | 034-governed-action-classification |
Reading this spec
Governed Action Classification is a design for changing how the constitutional governance layer decides whether an action is allowed. The spec identifies two structural weaknesses in the constitution: risk categories are enumerated name lists, which act as a blocklist so that the same operation under a new name passes, and consent is a category-global flag that cannot say "delete this test fixture automatically but never a production table". It also records that allow and deny logic in Python (gateway guards, the kernel dispatcher, the tool interceptor) is either advisory or bypasses the constitution entirely.
The spec replaces these with three things. First, classification derived from typed properties of an action descriptor and a resource descriptor. Second, authorization as a scoped grant matched to the specific action instance, from either a human-ratified standing policy rule or a per-instance human consent grant. Third, a single choke point: every allow or deny decision, including those made in Python today, routes through one Policy Decision Point, the constitutional Prolog gate, and every Python enforcement site becomes a thin Policy Enforcement Point that never decides locally. A single trusted reduction function turns a real operation (tool call, CXP packet, intent, gateway RPC) into the descriptor and asserts the ground facts the rules consume.
The sections cover the action and resource descriptor, property-derived classification, classification provenance (who may say what a resource is, so that an acting agent cannot label its own production delete as a test fixture), scoped authorization, incompleteness (an unknown action does not pass), the amendment direction (what counts as tightening or loosening), the one choke point, a status-honest audit of the current enforcement topology, the build backlog, the threat model and a decision register. Requirement IDs run REQ-034-001 to REQ-034-022, for example REQ-034-018 for the single Policy Decision Point.
No code module cites these requirements, so there are no tracing modules to link. The per-requirement view is on the RTM page.
The spec's status line reads Draft (design for review, not approved to build). It tracks 1 file and defines 22 requirement IDs. The full text follows below.
No src/ symbol cites any of the 22 requirements, so measured trace coverage is 0.0%. In tests/, 0 requirements are cited by a test-side @traces.
Coverage gaps: 22 requirements have no citing symbol. The largest untraced groups are REQ-034-* (22). Untraced means no @traces names the ID; the behaviour may still exist in code that was never annotated.
Requirements
Checkbox is the state recorded in the requirements checklist; Traced by is the count of src/ symbols whose @traces names the ID.
| ID | Done | Requirement | Traced by |
|---|---|---|---|
REQ-034-001 |
- | An Action is a typed descriptor, not an atom: | none |
REQ-034-002 |
- | A Resource is a typed descriptor: | none |
REQ-034-003 |
- | Descriptors are modelled in the canonical KG ontology first (the project's | none |
REQ-034-004 |
- | Each risk category is defined by the properties that constitute it, not by a | none |
REQ-034-005 |
- | The category taxonomy (defining properties, and is-a subtype edges such as | none |
REQ-034-006 |
- | A category rule is testable against properties: a regression asserts a | none |
REQ-034-007 |
- | A resource's classification attributes (class, env, owner, sensitivity) |
none |
REQ-034-008 |
- | Where classification cannot be read from structure, the order of trust is | none |
REQ-034-009 |
- | A verified test asserts the agent uid cannot relabel | none |
REQ-034-010 |
- | An action is authorized iff a grant's **selector matches this action instance's | none |
REQ-034-011 |
- | A standing policy grant is a human-ratified pattern rule that auto-authorizes | none |
REQ-034-012 |
- | A per-instance consent grant is bound to a specific resource (optionally its | none |
REQ-034-013 |
- | Article I (and the other consent-gated articles) consume authorized/1: |
none |
REQ-034-014 |
- | none | |
REQ-034-015 |
- | An action whose descriptor lacks an attribute a category rule needs is | none |
REQ-034-016 |
- | This is the safety net for ADR-0034's residual blocklist hole: property-derived | none |
REQ-034-017 |
- | Under ADR-0009 / ADR-0020, for SPEC-034 artifacts: | none |
REQ-034-018 |
- | Single PDP.** There is exactly one Policy Decision Point: the constitutional / | none |
REQ-034-019 |
- | No pure-Python policy.** A Python allow/deny that decides without consulting the | none |
REQ-034-020 |
- | Single reduction function (Python supplies the atoms).** One trusted-side function | none |
REQ-034-021 |
- | Audit-only is not enforcement.** Where the kernel dispatcher currently runs the | none |
REQ-034-022 |
- | Fail-closed reduction.** If reduce_to_action cannot classify an operation |
none |
Code that traces this spec
No src/ symbol cites a requirement of this spec.
Full spec text
Verbatim from specs/034-governed-action-classification/spec.md; headings are pushed down two levels.
SPEC-034: Governed Action Classification
Metadata
- Spec ID: 034-governed-action-classification
- Title: Governed action classification, scoped authorization, and single-choke-point enforcement
- Author: Mohamed
- Status: Draft (design for review — NOT approved to build)
- Created: 2026-07-24
- Priority: P1
- Components: Constitution (
prolog/constitutional.pl), governance package, canonical KG ontology (knowledge_graph/canonical_schema.py), the Python enforcement points (gateway policy gate / scope guard, kernel dispatcher/policy evaluator, tool interceptor), consent/grant stores - Depends on: SPEC-007 (prolog-governance), SPEC-033 (build factory — constitution tier, amendment authority), SPEC-106 (canonical semantic layer / ontology), SPEC-060 (enhanced PAM)
- Version: 0.1.0
Nature of this spec (read first). Prescriptive target, not documentation of existing code. Unbuilt components are planned tasks (§9), not defects. It is a design put up for ratification — the constitution and its authorization model are constitution-tier, so nothing here is built until Mohamed accepts the three ADRs (§11) that back it.
docs/adr/is authoritative for rationale. The three decisions this spec rests on are ADR-0034 (property-derived classification), ADR-0035 (two-source scoped authorization), ADR-0036 (classification provenance under the trust boundary). Where this spec and an ADR disagree, the ADR wins. References are inline as[ADR-NNNN].
Overview
What
Replace the constitution's two structural weaknesses — enumerated risk-category name lists and a category-global consent flag — with (a) classification derived from typed properties of the action and the resources it targets, (b) authorization as a scoped grant matched to the specific action instance, from two sources (a human-ratified standing policy rule, or a per-instance human consent grant), and (c) a rule that every allow/deny decision — including the ones made in Python today — routes through the one constitutional choke point, which is supplied its atoms from a single, trusted reduction function.
Why
- The name lists are a blocklist.
security_threat(bypass_auth).catches an action only if it arrives under a listed atom; the same operation under a new name passes every article. And nothing defines how a real operation becomes that atom — the reduction, where all the risk lives, is unwritten. [ADR-0034] - The consent flag is inexpressive.
human_consent_given(access_pii, _)authorizes every PII action forever for anyone. It cannot say "delete THIS test fixture automatically but never a production table" — the motivating case. [ADR-0035] - The classifier can be handed to the attacker. If the acting agent supplies the action/resource attributes, it labels its own production delete as a test fixture and the standing rule auto-authorizes it. Classification must be stamped by the resource's creator under the trusted identity. [ADR-0036]
- Python decides in the dark. Allow/deny logic scattered across gateway guards, the kernel
dispatcher, and the tool interceptor either audits the constitution (advisory) or bypasses it
entirely (hardcoded
if/allowlist). A decision the constitution never sees is an article it cannot enforce. (§7)
How
An Action descriptor and a Resource descriptor (typed, in the canonical ontology) are the only thing the gate reasons over. A single reduction function on the trusted side turns a real runtime operation (tool call, CXP packet, intent, gateway RPC) into that descriptor and asserts the derived ground facts the rulebase consumes. The constitution classifies by property, and composes the classification with a scoped-grant authorization check. Every Python enforcement point becomes a thin Policy Enforcement Point (PEP) that calls the one Policy Decision Point (PDP) — it never decides locally.
1. The action / resource descriptor (the thing being classified)
REQ-034-001 — An Action is a typed descriptor, not an atom:
Action{ id, verb, actor, targets:[ResourceRef], attributes:{...}, context }. verb is drawn
from a closed, ontology-defined verb set (delete, deploy, read, execute, modify_policy,
terminate_agent, ...); free-form verbs are unknown and route to incompleteness (§5), never to
allow.
REQ-034-002 — A Resource is a typed descriptor:
Resource{ id, class, env, owner, reversibility, sensitivity, provenance, version }. class and
env are closed enumerations in the canonical ontology (canonical_schema.py, SPEC-106) — e.g.
env ∈ {prod, staging, test, ephemeral}, class ∈ {user_data, system_config, governance_asset, test_fixture, build_artifact, ...}.
REQ-034-003 — Descriptors are modelled in the canonical KG ontology first (the project's
ontology-before-import rule), and the constitution reasons over a Prolog projection of them. The
.pl files hold rules over properties, not rosters of names.
2. Property-derived classification (replaces the name lists) [ADR-0034]
REQ-034-004 — Each risk category is defined by the properties that constitute it, not by a list of atoms:
%% BEFORE (rejected): security_threat(bypass_auth). security_threat(execute_untrusted).
%% AFTER:
security_threat(A) :- action_property(A, untrusted_execution).
security_threat(A) :- action_property(A, auth_bypass).
security_threat(A) :- action_property(A, audit_disable).
destructive_change(A) :- action_target(A, R), resource(R, reversibility, irreversible),
\+ authorized(A).
REQ-034-005 — The category taxonomy (defining properties, and is-a subtype edges such as
code_injection ⊑ untrusted_execution ⊑ security_threat) lives in the ontology. Adding a subtype
or a defining property is a governance change subject to §6 amendment direction.
REQ-034-006 — A category rule is testable against properties: a regression asserts a descriptor bearing property P is classified, and one lacking every defining property is not — the current lists have no such test because membership is definitional.
3. Classification provenance — who gets to say what a resource is [ADR-0036]
REQ-034-007 — A resource's classification attributes (class, env, owner, sensitivity)
are written by its creator at creation time under the trusted-side identity, and are
immutable to the acting agent identity (ADR-0018 boundary). The test harness stamps
class=test_fixture, env=test, owner=<test_run> when it creates a fixture; a production resource
is classified by its schema/migration/declaring system.
REQ-034-008 — Where classification cannot be read from structure, the order of trust is structural → detector-flagged-then-adjudicated → human (ADR-0029 high-recall detector, never a silent verdict; ADR-0014 promotes the adjudication into the KB). An agent's self-description is never a classification source (ADR-0008).
REQ-034-009 (acceptance, red-team) — A verified test asserts the agent uid cannot relabel a production resource as a test fixture by any route (KG write, Prolog assertz, descriptor field, env var). Until the ADR-0018 identity boundary is real (SPEC-033 B1d), this test is the gate on SPEC-034's enforcement claims — a classification store the agent can write makes the whole model cosmetic.
4. Scoped authorization — tying consent to instances and categories [ADR-0035]
REQ-034-010 — An action is authorized iff a grant's selector matches this action instance's
attributes. authorized/1 composes two sources:
authorized(A) :- auto_authorized(A). %% standing policy grant (human-ratified pattern)
authorized(A) :- matching_consent_grant(A). %% per-instance human grant, unconsumed & unexpired
REQ-034-011 — A standing policy grant is a human-ratified pattern rule that auto-authorizes a scoped class with no per-instance human touch. The motivating case is expressed directly:
auto_authorized(A) :-
action(A, verb, delete), action_target(A, R),
resource(R, class, test_fixture), resource(R, env, test).
Adding or widening such a rule is a loosening ⇒ human ratification (ADR-0009); the B16 amendment classifier flags it.
REQ-034-012 — A per-instance consent grant is bound to a specific resource (optionally its
content version), is single-use (scope=once), and carries provenance and expiry:
consent_grant(GrantId, who, verb, target, scope, version, reason, at). It is consumed on use
(the grant→action binding recorded in the B1c ledger / B20 attestation), and a version-bound
grant stops matching once the resource content changes.
REQ-034-013 — Article I (and the other consent-gated articles) consume authorized/1:
requires_human_consent(A), \+ authorized(A) → deny. The category-global human_consent_given/2
flag is removed.
REQ-034-014 (acceptance — the worked example, ratifiable):
deleteaclass=test_fixture, env=testresource →auto_authorized→ allowed, no human.deleteaclass=user_data, env=prodresource → no standing grant, no matching consent grant → denied; proceeds only with a matching single-use consent grant, which is then consumed.- The same fixture-delete on a
env=prodresource with a forgedclass=test_fixturelabel → denied by REQ-034-009 (agent cannot set the label).
5. Incompleteness — the unknown action does not pass [ADR-0011]
REQ-034-015 — An action whose descriptor lacks an attribute a category rule needs is
unclassified, and an unclassified sensitive action escalates, it does not default-allow. A
verb ∉ ontology or a class = unknown on a targeted resource is an incompleteness signal routed
to the B13 assumption/adjudication cascade, not a silent permit.
REQ-034-016 — This is the safety net for ADR-0034's residual blocklist hole: property-derived classification still can't catch a property nobody modelled, but the absence of a classification on a sensitive resource is itself detectable and denied-pending-adjudication.
6. Amendment direction (what's a tightening vs a loosening)
REQ-034-017 — Under ADR-0009 / ADR-0020, for SPEC-034 artifacts:
- Adding a category-defining property, adding a subtype edge, adding a
requires_human_consentverb → tightening (new denials) — quorum-eligible, but constitution-tier ⇒ human. - Adding or widening a standing
auto_authorizedselector, adding a resourceclassthat a standing grant covers, broadening anenvset → loosening (new acceptances) — human only. Boundary changes to what a class covers are loosening by ADR-0021. - The B16 classifier computes direction over the authorization edge set, not by keyword.
7. One choke point: Python allow/deny routes through the constitution
This section answers Mohamed's 2026-07-24 directive: "how python code that allows and denies must also go through the constitutional layer and possibly create atoms that supply the pl code." The current-state topology (which sites route through Prolog, which are audit-only, which bypass) is enumerated in §8 from the code audit; this section states the target.
REQ-034-018 — Single PDP. There is exactly one Policy Decision Point: the constitutional /
governance Prolog gate. Every allow/deny in the system is a Policy Enforcement Point (PEP) that
(1) builds no verdict of its own, (2) reduces its operation to an Action descriptor via the single
reduction function (REQ-034-020), (3) calls the PDP, (4) enforces the returned verdict. A PEP
contains no policy — no allowlist, no denylist, no hardcoded if action == ....
REQ-034-019 — No pure-Python policy. A Python allow/deny that decides without consulting the PDP is a governance bypass and is a defect regardless of authorship (§8 lists the current ones). Permitted Python-local rejects are limited to non-policy failures (malformed input, transport error, auth identity failure) — these deny-closed and are distinct from a policy verdict. A Python site may pre-reduce and cache, never decide.
REQ-034-020 — Single reduction function (Python supplies the atoms). One trusted-side function
reduce_to_action(op) -> ActionDescriptor turns each operation kind — tool call
(ToolInterceptor / claude hook), CXP packet (kernel dispatcher), intent (orchestrator), gateway
RPC (policy gate / scope guard) — into the typed descriptor, and one assert_action_facts(desc)
projects it to the ground Prolog facts the rulebase reads (action/3, action_target/2,
action_property/2, resource/3). This is the only place atoms are minted for the gate.
Consequences:
- The reduction is written once, reviewed, and testable — not scattered ad-hoc across call sites (the status quo, §8).
- Facts are asserted from outside the rulebase (ADR-0005), ground, and test_id-scoped /
retracted (repo fixture discipline); rules never
assertz(ADR-0025). - The agent-supplied part of an operation contributes only the verb and target references; the classification attributes come from the trusted resource store (REQ-034-007), never from the operation payload.
REQ-034-021 — Audit-only is not enforcement. Where the kernel dispatcher currently runs the PolicyEvaluator in AUDIT-ONLY mode (records the verdict, does not act on it), promotion to enforcing is a governance change: fail-closed on deny, with the SPEC-033 liveness posture (interactive sessions fail-open, factory gates fail-closed — ADR-0002) preserved. The shadow → enforce transition is itself ratified.
REQ-034-022 — Fail-closed reduction. If reduce_to_action cannot classify an operation
(unknown verb, unresolvable target), the PEP treats it as §5 incompleteness — escalate/deny
pending adjudication in a factory context; in an interactive context, the ADR-0002 fail-open
applies but the unclassified action is logged as a governance gap.
8. Current-state enforcement topology (audit — status-honest)
Populated from the code audit. Each site is classed routes-through-Prolog (PDP-backed), audit-only (advisory), or pure-Python bypass (decides without the PDP). Bypasses and audit-only sites are the SPEC-034 backlog in §9; this is a map of what exists, not a list of defects to fix silently.
Structural finding (the load-bearing one). There is no reduction function mapping a real
runtime operation to the constitution's atom vocabulary. constitutional_check/3 reasons over a
hardcoded vocabulary in constitutional.pl (requires_human_consent/1 L73-79, involves_pii/1
L169-171, security_threat/1 L232-236, destructive_change/1 L299-301). The "Action atom" it
sees is the raw action string the caller happens to pass, formatted by
prolog_client._format_atom (L186). Unless a caller literally passes action="delete_data", the
check matches the permissive default clauses (Articles VIII–X are bare true;
accountable_operation is always true) and never denies. So the constitution is, in practice,
structurally incapable of firing on real actions — exactly the ADR-0034 "the reduction is
undefined" hazard, now demonstrated. This is the single highest-value SPEC-034 target (B34-5).
Three parallel, weakly-connected rulebases exist: constitutional.pl (via PolicyEngine),
governance.pl evaluate_policy/3 + may_execute/2 (via the orchestrator engine and
GovernancePrologClient), and policy_evaluator.py policy_permits/4/tool_allowed/4
(audit-only). Each builds its terms from raw strings independently — the "scattered, per-rulebase
reduction" the single reduction function (REQ-034-020) replaces.
Routes through Prolog (PDP-backed, authoritative)
governance/policy_engine.py::PolicyEngine.evaluate(L85) — THE orchestration choke point: safe_mode → cache → constitutional (L160) → ethical (L201) →prolog_client.may_execute(L239); fails closed undergovernance_strict(default true).governance/constitutional.py::ConstitutionalEnforcer.check_article(L139) →constitutional_check(L152), fail-closed;requires_human_consent(L292) →query_bool(L303).governance/prolog_client.py::GovernancePrologClient— all 10 predicates → janus; error branches resolve viagovernance_strictwith amake_verdictprovenance record.orchestrator/engine.py::UOxEngine._evaluate_intent(L488) →governance:evaluate_policy/3(L509), fail-closed DENY on error/no-match — but preceded by a hardcoded Python deny (below).- Gateway RPC path —
_policy_gate.py::PolicyGate.check(L47) →engine.evaluate(L69), raises on deny — only when the factory PolicyEngine is wired (server.py::_make_policy_engineL480); permissive when no factory (L66-67).
Audit-only (Prolog runs; verdict never blocks)
kernel/dispatcher.py::_audit_evaluate_routing(L310) — the named AUDIT-ONLY wiring; active only whenpolicy_audit_mode=True(default False), and even then only logs (docstring L194-199: "pure observation, never blocks"). → B34-6 promotion.kernel/policy_evaluator.py::PolicyEvaluator— real januspolicy_permits/4(L595),tool_allowed/4(L790); fail-closed logic exists but is never enforced (only consumer is the audit path).orchestrator/verification_gate.py::VerificationGate—enforce=Falseby default; verdicts attached, never fail a run (and its predicates are coverage/lint, not constitutional).
Pure-Python bypass (never consults governance/Prolog) — the SPEC-034 backlog
core/adapters/claude/tool_interceptor.py— the agent tool-call path._check_lists(L150) decides by Pythonsetmembership;_check_governance(L178) is a no-op when no engine (L191). The shipped wiring (hooks.py:89,core/adapters/__init__.py:43) constructsToolInterceptor()with no engine → every agent tool call defaults to ALLOW, reaching neither the constitution nor any rule. Top target (B34-5).core/adapters/claude/gate_policy.py—SELF_EXCLUSION_SET(L111) hardcoded(tool, path)frozenset checked FIRST (bypass surface);resolvedecides from aFailurePolicyenum, no governance.orchestrator/engine.py—PROTECTED_PATHS(L175) hardcoded;_is_protected_pathDENYs at L501 before Prolog is queried;_parse_intent_from_context(L431) is a hardcoded tool→Action map.orchestrator/intent_security.py—DANGEROUS_ACTIONSfrozenset (L119) + regex denylists (L73-118); consults Prolog only if a bridge was injected AND the action is in the frozenset.gateway/handlers/cxp.py::handle_send(L109) — builds + broadcasts a CXP packet with no authority check;postman/governance.py::check_send_authorityexists but is not called here.postman/governance.py::PostmanGovernanceManager— decides via Python staticAGENT_PERMISSIONSdict (L86);U:Ox→ universal allow (L204). A janus engine +postman_governance.plexist but the decision path uses the dict, not the rulebase.gateway/handlers/_scope_guard.py::ScopeGuard.granted_scope(L64) — returns a flat hardcoded"read_write"for every connection; pure Python rank comparison, no Prolog.governance/resource_access.py— Python fallbacks decide when the mode isn't a valid Prolog atom or the query errors (DEFAULT_NETWORK_WHITELISTL110, "GET always allowed" L147).
Fail-open / error-allow holes (deny-closed elsewhere, open here)
governance/prolog_gate.py::PrologGate.may_execute(L82) — ERROR_ALLOW: if rules aren't loaded, returnsTrue("permissive mode", L96-105).governance/ethical.py::EthicalBoundary— fails OPEN: any Prolog failure returnshas_harm=False(L112-118, L139, L158, L177, L196), so the ethical layer never blocks on error.
Atom supply today (who asserts ground facts to the constitution)
- The only runtime ground-fact assertion into the constitutional rulebase is
governance/constitutional.py::human_override(L264) →human_override_active/3(L276) +human_consent_given/2(L281) viabridge.assert_fact. - The
:- dynamicevidence predicates the articles depend on —pii_data/2,bias_report/3,audit_trail_exists/1,detect_bias/3— are declared but never populated from Python (zero non-testassert_factcall sites).orchestrator/prolog_bridge.py::assert_fact(L304) is the generic(predicate, args)→ ground-term reducer, but it targetsgovernance:/knowledge:, not the constitutional evidence base. This vacuum is precisely what REQ-034-020'sassert_action_factsfills, fed by the trusted reduction (never by the agent payload).
9. Build Status & Implementation Backlog (tasks, not gaps)
Nothing in this section is built. This spec is for ratification first. On acceptance, the backlog is:
- B34-0 — Enforcement-topology audit (read-only). DONE — §8 filled from a code audit: 3 parallel rulebases, the missing reduction function, the default-engineless tool interceptor, 8 pure-Python bypass sites, 2 fail-open holes, and the atom-supply vacuum.
- B34-1 — Descriptor ontology. Action/Resource typed descriptors + closed
verb/class/envenumerations + risk taxonomy incanonical_schema.py(SPEC-106). [REQ-034-001..006] - B34-2 — Property-derived category rules. Rewrite
security_threat/1etc. as property rules; delete the name lists; property regression suite. [ADR-0034] - B34-3 — Classification provenance. Creation-time stamping under the trusted identity; agent-immutable classification store; the REQ-034-009 red-team test. Gated on SPEC-033 B1d (identity boundary). [ADR-0036]
- B34-4 — Scoped-grant authorization.
auto_authorized/1standing rules + per-instanceconsent_grantstore +matching_consent_grant/1+ consumption/attestation; removehuman_consent_given/2; the REQ-034-014 worked-example acceptance suite. [ADR-0035] - B34-5 — Single reduction function + PEP conversion.
reduce_to_action/assert_action_facts; convert each §8 PEP to call the one PDP; remove pure-Python policy. [REQ-034-018..020] - B34-6 — Promote audit-only to enforcing. Kernel dispatcher PolicyEvaluator shadow→enforce, fail-closed, liveness-preserving. Separately ratified. [REQ-034-021]
- B34-7 — Amendment-direction wiring. B16 classifier understands selector widening / class boundary as loosening. [REQ-034-017]
- B34-8 — Incompleteness routing. Unknown verb / unclassified sensitive resource → escalate-not-allow. [REQ-034-015..016]
10. Threat model & failure posture
- Synonym escape (new operation, unlisted name) — closed by property-derived classification (§2) and, for the residual, by incompleteness-denies (§5).
- Label forgery (agent claims
test_fixture) — closed by creation-time provenance (§3), verified by REQ-034-009. - Consent over-reach (one grant authorizes everything) — closed by instance-scoped, single-use, version-bound grants (§4).
- Gate bypass (Python decides locally) — closed by the single-PDP rule (§7); §8 enumerates the current bypasses.
- Selector over-broad (standing rule matches prod) — treated as a loosening (human) and tested like a rule (§6); a mis-scoped selector is a privilege escalation, so selector tests are acceptance-blocking.
11. Decision register
- ADR-0034 — Classification is property-derived, not name-enumerated.
- ADR-0035 — Authorization is a scoped grant matched to the action instance, from two sources.
- ADR-0036 — Classification facts are stamped at creation under the trusted boundary.
All three Proposed, awaiting human ratification (constitution-tier). SPEC-034 is not built until they are accepted.