Concepts rapidvalue.eu →

Approver Chains

Multi-step and quorum approvals — one canonical shape shared by approval rules, access-review rules and the reviewer-chain registry.

One canonical shape, shared by all consumers

ApprovalRule.then_branch / else_branch and CertRule.reviewer_chain share the same JSONB schema since Sprint cert-redesign Phase 2 (migration 0133 converted legacy flat lists):

{
  "steps": [
    {
      "sequence": 1,
      "quorum": "all",
      "approvers": [{"kind": "manager"}]
    },
    {
      "sequence": 2,
      "quorum": "any",
      "approvers": [
        {"kind": "approver_role", "value": "security"},
        {"kind": "approver_role", "value": "compliance"}
      ]
    }
  ]
}

Approver kinds — who becomes an approver?

Which kinds are valid for a given chain depends on its target: the step-vocabulary endpoint (GET /api/v1/reviewer-chains/step-vocabulary?target=<key>) drives the editor dropdowns — the facets come live from the ownership registry, so the vocabulary never drifts from what is resolvable.

Reviewer-chain registry — named chains + one resolver

Since Sprint reviewer-chain-registry (2026-06-10) a reviewer_chain row is a named, reusable chain in the same {steps:[…]} shape. A chain can also be the default for a key (default_for, e.g. cert:grant or approval:access_request). Every consumer (approval rules, access-review rules, role formalization) resolves through one resolver, in a fixed order:

inline override → named chain (FK) → default-per-key → manager fallback

A rule without a chain therefore always gets a reviewer (never a silent no-reviewer). Two built-in chains are seeded per tenant: rc-manager-default (default for grant / identity-validity / access-request) and rc-composition-owners (default for role / policy / ownership — the object owner for the whole object, then item owners per item). The per-step scope (whole_object / per_item) drives the composition fan-out.

Quorum — how many must approve?

Multi-step — sequence semantics

Steps are sequential — step 2 only spawns once step 1's quorum is reached. Lazy step activation happens in _advance_or_finalize:

Delegation (out-of-office)

ApprovalDelegation (Sprint m34y) lets approvers temporarily hand over their decision-making authority. The period and scope are set per rule kind. The resolver follows active delegations at task-spawn time — the delegate receives the decision task in their inbox with a "delegated by X" badge.

Fan-out per approver

Each step spawns a separate decision task per approver, explicitly grouped via fan_out_group_id. Group quorum is evaluated on every decision; the step is complete as soon as the quorum is reached. Fan-out gives per-approver visibility plus a clean audit trail (who decided what, and when).


Further reading: