Open Standard · Working Draft
mekaniskt·kontrakt

Worked examples ·  07 of 07

The hospital master

A standard hospital staffing template is instantiated through a chain of binding stages - the organization binds its own parties once, then each engagement binds the rest - instead of being copy-pasted and forked.

SIZE
small, structural
PROFILE
any
STRESSES
Genesis as a chain of binding stages, party slots bound late under declared constraints, and upgrade-by-transform instead of fork-and-drift.

Staged binding is the whole feature. Today an organization adapts a standard contract by copy-pasting the Word file and editing - a fork: it never receives upstream fixes, cannot be diffed against the standard, and drifts silently. Staged binding replaces the fork with a reference plus bound variables: the hospital’s “our contract” is a partially-applied artifact, still hash-linked to the governed template it came from.

The formulation

A contract is an expression over declared parameters. A template has free variables; a fully-ground instance has none. Genesis generalizes from one event to a chain of binding stages, each signing a subset of the remaining free variables, each hash-referencing the previous stage. (Technically partial application rather than currying - stages bind arbitrary subsets in any order, which is what contracts need. Schönfinkel, 1924, got there first.)

Law got there before software did:

The ladder Stages
ISDA Master → per-trade confirmations parties bound once, economics bound per trade
Adda ramavtal → kommun call-off → avrop supplier signs a quantified buyer slot (“any eligible member”); the kommun binds itself later; quantities bind last
Kollektivavtal → hängavtal → anställningsavtal the same ladder in labor law
LOU förnyad konkurrensutsättning a multi-supplier framework where each call-off’s mini-competition binds which supplier fills the party slot - runtime party binding, competitive, already Swedish law
A webshop template awaiting a buyer a standing offer IS a signed partial application (example 01 was one all along)

The contract chain

# ── STAGE 0: the governed template (a §5 artifact; e.g. profile- or SKR-published)
mechanical-contract v0  TEMPLATE
id: tpl-se-health-staffing @ v4
parameters:                              # each slot declares: type/domain,
  vardgivare  party<org>                       bound-by stage org-master
  attest      artifact<attestordning>          bound-by stage org-master
  calendar    artifact<calendar>               bound-by stage org-master
  supplier    party<org> in {vardgivare's approved-supplier register}
                                               bound-by stage engagement
  rates       lookupOf<role, money/h>          bound-by stage engagement
  volume      quantity<h>                      bound-by stage engagement
# ports, pools, cadences, escalation: written ONCE, here, over the slots

# ── STAGE 1: the hospital master - the org binds ITS variables, once
binding-stage region-skane-staffing-master:
  of tpl-se-health-staffing @ v4  sha256:...
  bind vardgivare  org 232100-0255 "Region Skåne"
  bind attest      art:skane-attestordning   track latest-approved
  bind calendar    art:se-working-days       track latest
  signed: Region Skåne (eIDAS-qualified-seal)      # ONE party signs: this is a
                                                   # standing offer / internal
  # remaining free: supplier, rates, volume        # standard, not yet an agreement

# ── STAGE 2: each engagement - thin, confirmation-shaped
genesis mk-se-health-2027-0142:
  of region-skane-staffing-master  sha256:...
  bind supplier  org 556677-8899 "Bemanning AB"    # constraint CHECKED at binding:
  bind rates     art:bemanning-rates-2027  pin v1  # not in the approved register ->
  bind volume    5000 h                            # binding REJECTED, never a stage
  signed: both                                     # (§5.2's illegal-update rule,
                                                   # applied to binding)

Every staffing, equipment, and cleaning contract the region signs afterwards starts from stage 1 - identity, attestordning, calendar, profile inherited by reference. When the template ships v5, the master adopts it via one §6.4 transform (adherence, receipted); every future engagement inherits automatically, every existing one stays pinned per §6.3. The forked Word document can do none of this.

The rules that keep it boring

  • Each stage signs the constraint, not the value. The Adda supplier never met Lunds kommun; they signed buyer in eligible-members. A slot’s domain is a contract term of the earlier stage; violating it at bind time is a rejected event, never a stage.
  • A one-signature stage is an offer; a both-signature ground contract is an agreement. The signature set per stage is declared per slot (signs:), so what the artifact legally IS at each rung is readable off the manifest.
  • Depth terminates structurally. The parameter list is finite and every stage must bind at least one variable, so chain depth ≤ parameter count. Once ground, nothing remains to bind - no recursion possible.
  • Width is a pool draw. The other reading of “instances creating instances” - a contract whose rules spawn siblings (on order.accept: instantiate ...) - is leashed by the existing machinery: instantiation-as-effect consumes declared capacity from the parent (the avrop draws the takvolym; a merchant master draws a max-active-orders pool). Unbounded self-replication is a §3.1 boundedness question the linter refuses. Daml’s create-in-choice proves the mechanism; Solidity’s factory sprawl is the graveyard we route around.
  • Stages do not freeze the environment. A closure captures its environment; a binding stage must NOT silently snapshot artifact versions - effective-dating (§5.1) keeps ruling, and pins are explicit per binding. A stage that quietly froze the rate table would be a new species of silent term.

What this example stresses

Edge Spec section
Genesis as a chain of binding events §6.3 (extended)
Party slots bound late, under declared constraints absorbs the besiktningsman appointment and the party-role questions
Standing offers as one-signature partial applications §6.3, avtalsrätt
Instantiation-as-effect bounded by pool draws §3.1, §7.1
Upgrade-by-transform instead of fork-and-drift §6.4

Questions this example forced

  • Staged binding as THE instantiation model. As above: parameters declare domain + binder + signer per slot; stages chain by hash; constraint-violating bindings are rejected; depth terminates by arity; width draws pools. Absorbs the appointed besiktningsman (a party slot bound mid-contract) and gives the role classes their mechanism. Resolved in the working draft as the instantiation model; spec text pending (§6.3 extension).
  • Derived values are views, and “reactive” needs a guard rail. Surfaced by the aggregate discussion: since ports, payloads, and bindings are immutable, every computed value (subtotal, consumed %, vite accrued) is a pure function of a log prefix - so computed things are never stored, never signed alone, and may be declared as named, typed derived values (the expectation register was the prototype; the invoice-as-projection is one rendered to a document). Reactivity split: as evaluation strategy (memoization, incremental maintenance - materialized views, Jane Street Incremental, Adapton/Salsa; purity makes caching undetectable, hence legal) always allowed; as semantics (derived-value changes triggering behavior spontaneously) forbidden - predicates evaluate AT events and heartbeats only, or replay dies. Division/percentages inherit declared rounding always, and must go through split the moment results move money.

Tests

These behaviour tests exercise the contract above. Because the contract is executable it is testable: the same machine that evaluates it runs the tests - Gherkin to read, JavaScript to run.

Feature: Hospital staffing - staged binding
  Scenario: An engagement binding an approved supplier grounds the contract
    Given "Bemanning AB" is in Region Skåne's approved-supplier register
    When the engagement binds supplier "Bemanning AB"
    Then the binding is ACCEPTED

  Scenario: A supplier outside the approved register is refused at bind time
    Given "Rogue Staffing AB" is not in the approved-supplier register
    When the engagement binds supplier "Rogue Staffing AB"
    Then the binding is REJECTED
    And the trace mentions "not in approved register"
import { test, expect } from "bun:test"
import { createContractMachine } from "@mechanical-contract/test"

test("a supplier outside the approved register is refused at bind time", () => {
  const mk = createContractMachine("region-skane-staffing-master")
  const r = mk.bind({ supplier: "Rogue Staffing AB", volume: "5000" })
  expect(r.rejected).toBe(true)
  expect(r.trace).toContain("not in approved register")
})

test("an approved supplier grounds the contract", () => {
  const mk = createContractMachine("region-skane-staffing-master")
  const r = mk.bind({ supplier: "Bemanning AB", volume: "5000" })
  expect(r.accepted).toBe(true)
})