ADR-0097: A Governed Provisioning Surface — MANAGE_SCOPES¶
- Status: accepted
- Date: 2026-07-27
- Deciders: Nicolas Burri ("D1: build now please · D2: global permission is also my preferred solution · D3: confirm"), resolving the decision the setup-wizard slice surfaced and deliberately did not take.
- Relates to: the wizard (D-1b..D-4 — the hand-off panels these endpoints replace), ADR-0095 (key-rule governance the write path implements), ADR-0096 (the grant model provisioning must honor), ADR-0002/0010 (mandator model), the seed-vs-wizard oracle (D-5).
- Amended by: ADR-0109 §5 (MANAGE_MANDATORS splits out — organization founding becomes its own operator-only process), ADR-0122 §4 (CREATE_PROJECT splits out too, checked at the target organization's anchor — so §2's "one permission gates all four acts" no longer holds: MANAGE_SCOPES keeps scope creation below the org, type/role activation and key rules)
Context¶
Four provisioning acts had no REST write surface — creating a mandator/scope, activating a document TYPE in a scope, activating a ROLE in a scope, seeding KEY RULES. All were control-plane SQL reachable only by the seed and the importer, so "a project can be born in the tool" (v0.9.0 criterion 2, Nicolas's original wizard ask) was only half true: the wizard configures, but cannot create.
Decision¶
- Build the four write surfaces now — governed REST endpoints the wizard's existing hand-off panels convert into live steps.
- One new GLOBAL permission:
MANAGE_SCOPESgates all four acts — they are one authority in practice ("shape the installation"). Dedicated permission per the house pattern (VIEW_OBLIGATIONS / CONFIGURE_REVIEW_POLICY precedents); a creation-vs-activation split is deliberately NOT made until a real persona needs it (least mechanism). Callers without it keep the wizard's "hand to an administrator" panels — the hand-off shape stays as the unauthorized rendering, never a silent skip. - Every act is audited, GLOBAL-anchored, before/after:
MANDATOR_CREATED,SCOPE_CREATED,TYPE_ACTIVATED,ROLE_ACTIVATED,KEY_RULE_SET— the established audit pattern, nothing novel; names not UUIDs in any message (house rule). - Non-decisions made explicit: the key-rule write implements ADR-0095's governance unchanged (free until first use, then frozen, audited exception). The control-plane posture is unchanged (permission-governed, deliberately outside RLS). The SEED keeps its SQL path — bootstrap runs before any authenticated user exists; the D-5 oracle guards seed-vs-wizard parity. Importer alignment onto the endpoints is a registered follow-up, not part of this slice.
Amendment 2026-07-30 — one governed scope UPDATE (feedback register finding #3)¶
§4 stated the surface was additive: no scope UPDATE was published, so "correct a flag of a scope that
already exists" had no governed path (the importer kept it as SQL). The play-test found the cost on the
other side: may_be_derive_source was asked at BIRTH, where it is unintuitive and almost always false.
Decision (Nicolas, register finding #3): the flag leaves the birth form (server default false) and becomes
a governed, changeable setting — PUT /api/admin/provisioning/scopes/{scopeId}/settings, the same
MANAGE_SCOPES-at-GLOBAL gate, audited GLOBAL-anchored as SCOPE_SETTINGS_CHANGED with before/after.
Editable in BOTH directions at any time, because enforcement is act-time only: the flag is read inside
the derive's own SECURITY DEFINER self-gate (V017), so turning it off refuses the NEXT derive and touches
no copy already derived. The GLOBAL base scope is refused (the bootstrap's, and every project's source),
a value already set is a 204 no-op that records nothing, and nothing else about a scope becomes editable —
code stays immutable (V001), may_hold_records follows the scope type (register 2026-07-28 finding #7).
Consequence: the importer's last scope UPDATE in SQL is converted onto this endpoint.
Consequences¶
- Migration: the
MANAGE_SCOPESpermission row + grants to the administrator-class roles per the existing permission-seeding pattern; the wizard's four panels gain their live forms (permission- reflected); setup-status/staffing preview compose unchanged. - v0.9.0 criterion 2 becomes fully true: mandator → project → configured → staffed, all in-tool.
- Separation tests extend to the new endpoints (control-plane authorization, not RLS).