Open Standard · Working Draft
mekaniskt·kontrakt

Specification ·  Section 4 of 13

Approval and escalation

Covers how approval (the attest), escalation (throw/catch/finally), and fault handling are declared and enforced inside the contract rather than left to the surrounding systems.

4.1 Approval: the attest, made first-class

Swedish public sector already runs on this - every kommun has an attestordning (approval hierarchy, mandated by internal-control rules) and the tvåhandsprincipen (four-eyes). Today it lives in the ERP, disconnected from the contract. The mechanical contract pulls it INTO the agreement:

  • Legs, not modes. A port’s rules decide validity; the approval block decides the leg: below 50k SEK -> straight-through (the STP leg - valid means done, immediately); mid-range -> one attestant; big -> four-eyes. The thresholds are contract terms - signed, diffable, litigable - not ERP configuration someone quietly changes.
  • What approval MEANS is declared, not assumed. An approver is a slot bound to a policy: a named human role signing with eIDAS, or an agent - a signing key bound to a hashed, versioned policy document. “AI approval” is not hand-waving: it is a keypair whose decision policy is content-addressed in the contract, so which robot judgment was delegated, and under which policy version, is itself signed and auditable. Change the policy, that’s an amendment. (This is the robot-economy door from §3.3: at machine speed, both counterparties AND approvers are agents - same primitive, faster cadence.)
  • Determinism survives because the machine never decides approvals - it waits, deterministically. An approval is just another signed event through the approval.grant port; “pending approval” is a first-class state (visible in the picture: money sitting between CONSUMED and INVOICED-approved is the attest backlog, per attestant, for free). Replays reproduce exactly which approvals happened and when.
  • Referee mode vs endgame: today straight-through means “auto-recommend attest into the ERP” - the human rail stays. The day payment.instruct opens (the last-capability rule), the STP leg becomes literal immediate payout - and the approval primitive is precisely what makes switching that on safe: automation happens only on legs the contract explicitly declared straight-through, with everything else gated exactly as signed.

4.2 Escalation: throw / catch / finally

Real contracts already contain the catch-blocks - they’re called eskaleringstrappa / multi-tiered dispute resolution clauses (negotiate -> steering group -> mediation -> arbitration; NEC4’s early-warning-then-adjudication ladder is the famous one). And they already contain the finally-blocks - survival clauses (sekretess, warranties, return of materials: obligations that run no matter HOW the contract ends). What no one has done is give them execution semantics:

throw    a rule or guard raises a condition it cannot resolve locally:
         pool-exhausted, obligation-breached, N-times-rejected, approval-timeout
catch    declared escalation LEVELS - each names who is summoned, what gets
         suspended, and a resolve-within deadline; missing the deadline
         throws to the next level (deadlines all the way up)
finally  survival obligations that execute on ANY exit - expiry, termination,
         level-4 collapse - tracked like every other obligation

Why this beats if/else spread through the rules: escalation is a cross-cutting concern. Pool exhaustion can arise in three different rules; without structured escalation each rule hand-rolls its own “what now”, and the contract’s crisis behavior is smeared across the document. With it, rules stay clean single-purpose guards, and the ENTIRE crisis ladder sits in one signed, reviewable block - which is exactly how the lawyers already drafted it in prose. The mapping to §15/§17 of the prose is 1:1; we are giving the eskaleringstrappa a runtime.

Mechanically, escalation levels are control states (the finite control of §3.1 - active ⇄ suspended was already there; levels refine it), entered and exited by events, with everything visible in the picture: a contract at level 2 renders as suspended port + who owns the clock + days left on the resolve-within deadline. Escalations are the off-track alarm made contractual.

Prior-art honesty: BPMN 2.0 literally has throw/catch escalation events and error/compensation events as first-class constructs - business-process people solved this pattern’s semantics; nobody attached it to a signed agreement. Tiered dispute-resolution clauses are standard legal drafting - all prose, zero runtime. The saga pattern (compensating transactions) is the distributed-systems cousin of finally. The composition into a contract runtime is, again, the novel part.

4.3 Faults: when the machinery fails, as a first-class mechanic

Escalation (§4.2) handles contractual crises - pool exhausted, obligation breached. A fault is categorically different: the machinery failed or the artifact was defective, so no judgment on the merits ever happened. The distinction is not cosmetic, it is load-bearing:

  • FAULTED joins the verdict enum (the envelope’s closed set grows once, deliberately: ACCEPTED / REJECTED / PENDING / ESCALATED / FAULTED). REJECTED means judged against the contract and failed; FAULTED means never judged.
  • Faults move nothing. A fault receipt always has state-after == state-before, consumes no cadence counters (a supplier whose invoice FAULTED on a dead tax service has NOT used their one-invoice-per-month), and leaves the submission-id retryable - resubmission after a cleared fault gets a real judgment, not the fault receipt replayed. Receipts about the attempt, not the state.
  • Every fault names its at-fault domain - because consequences differ legally, not just operationally:
Fault class Examples Attributed to Default consequence
Input fault schema-floor failure, invalid signature, unresolvable reference submitter FAULTED receipt citing the gate; submitter’s clocks keep running (your defect, your delay)
Import/executor fault deferred call-out timeout, oracle output failing its guards, inline wasm trap, binding unresolvable the bound import/executor party fault-policy runs (below); §3.6 expectation ladder throws if exhausted
Infrastructure fault host unreachable, heartbeat silent, §3.5 conflict-retry exhaustion the host blame suspension, declared: obligations whose performance required the machinery don’t accrue dröjsmålsränta during an attributed host fault - kontrollansvar (köplagen; CISG art. 79’s impediment-beyond-control) as an executable carve-out, in the contract, not litigated after
Contract-logic fault the “impossible”: an invariant the linter guarantees violated at runtime, bound-exceeding arithmetic the contract itself (drafting) fail fast + contain: affected port auto-suspends, escalation summons the parties, resolution is an amendment - never a hotfix to a signed document
  • Fault handling is DECLARED, like everything else. Per port and per import, from a closed verb set: retry (bounded, safe because idempotent), fallback (alternate binding, if declared), park (PENDING + expectation), escalate (throw into §4.2’s ladder), halt (close the port pending amendment). And the circuit breaker becomes a contract term: fault-policy: after 3 consecutive import faults -> open 24h, park submissions, notify both parties - Nygard’s pattern, signed. The linter enforces that every deferred import carries a fault policy or inherits the profile default; open circuits and fault storms are visible in the expectation register (§3.6).
  • Containment is free, by earlier decisions. The fold is pure, so a fault cannot corrupt state (there is no mid-transaction state to corrupt - §3.5’s effects-only-after-commit); and one contract faulting cannot touch a sibling, because nothing sits between contracts (§7.3b’s no-middleware rule paying rent again). Erlang’s let-it-crash philosophy arrives here with its work already done: crash small, record, let the declared supervisor - the fault policy, then the escalation ladder - decide.

Prior art: Erlang/OTP supervision trees (declarative fault handling: child fails, supervisor’s declared strategy restarts/escalates - the fault-policy verb set is a supervision strategy for contracts), the circuit breaker (Nygard, Release It!), dead-letter queues (park-with-attention for poison messages), wasm traps (deterministic failure of sandboxed imports - a trap is a value, not chaos), RFC 9457 Problem Details (the standardized machine-readable fault body - our fault receipt’s body schema rides it), the IEEE fault → error → failure chain (the vocabulary discipline), and on the legal side kontrollansvar/CISG art. 79 - the law has always known that whose machinery failed changes who pays; it just never had attribution receipts.