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

ADR-0035: Authorization is a scoped grant matched to the action instance, from two sources

field value
Status Proposed
Date 2026-07-24
Register factory (docs/adr, 4-digit ids)
Source docs/adr/0035-scoped-authorization-two-grant-sources.md — permalink at bdafa201a (unchanged on master since that commit)
Wiki register entry ADR-0035 (wiki status: Proposed)

← ADR-0034 · ADR-0036 →

The record

Context

Consent today is a single fact keyed on the category with a wildcard actor:

respects_privacy(Action, compliant) :- involves_pii(Action), human_consent_given(access_pii, _).

human_consent_given(access_pii, _) authorizes every PII action, forever, for anyone. It cannot express the distinctions real governance needs, of which the motivating case is:

No data in production may be deleted; but test data placed there by a test run, to be cleaned up after the test, is deleted automatically.

The same verb (delete) must deny one instance (prod, human-gated) and auto-allow another (test fixture, no human), and a category-global consent flag cannot represent either the instance scoping or the conditional auto-authorization.

Decision

An action is authorized iff some grant's selector matches THIS action instance's attributes. Authorization is not a category flag; it is attribute-based, and comes from exactly two sources:

  1. Standing policy grant — a human-ratified pattern rule that auto-authorizes a scoped class of actions with no per-instance human touch:
    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), flagged by the B16 amendment classifier. It is a pattern in the SPEC-033 tier sense (grows by human ratification), ratified once, then automatic.
  2. Instance-scoped consent grant — a human grant bound to a specific resource (optionally its content-version), single-use, carrying provenance and expiry:
    consent_grant(GrantId, who=<human>, verb=delete, target=<resource>,
                  scope=once, version=<v>, reason=<...>, at=<...>)
    

The article check composes them and the constitution consumes the result:

authorized(A) :- auto_authorized(A).
authorized(A) :- matching_consent_grant(A).      % unconsumed, unexpired, selector matches
%% Article I:  requires_human_consent(A), \+ authorized(A)  ->  deny

Alternatives rejected

Consequences

Provenance

Mohamed, 2026-07-24: "how do we tie consent to specific instances of actions? and specific categories? e.g. no data in production to be deleted but test data ... deleted automatically." — the worked example that exposed the category-global consent flag as inexpressive.

Rulings cited

None.

Specs cited