Code as a Contract — a manifesto

Code is not instructions to a machine that humans happen to read. Code is a contract between many parties — the customer, the business, the payment processor, the next developer, the on-call engineer at 3am — that a machine happens to execute.

Read any well-drafted legal contract and you find the same structure good code wants: definitions first, then sections stating each party's obligations, responsibilities and abilities, the business rules and how they are applied, and the succession of events — what must happen, in what order, and who has to do it.

We hold these principles:

  1. Definitions come first, and are declared, not implied. Types are the contract's Definitions section. A party, an amount, a deadline each gets a name and a shape before any clause uses it.
  2. Every business rule has a name. A rule that exists only as an inline if is an unwritten verbal agreement — unenforceable and disputed later.
  3. The operative clause reads top-to-bottom. The main function of an interaction has minimal — ideally zero — cyclomatic complexity. It barely does anything itself: it calls named functions that abstract the internals of the contract. Anyone can read it aloud in a meeting.
  4. Terms are pure; execution is separate. Deciding what the contract says is a pure function of facts. Performing the contract (charging cards, writing rows, sending mail) happens at the edges, after the decision. You can judge the terms without a courtroom.
  5. Tests are case law. Each pure unit test is a scenario a party could dispute, named as a claim, settled by the contract at the highest level.
  6. The principle applies recursively — without abuse. Implementations of the named functions follow the same discipline, but splitting stops when a function's body is plainer than another name would be. A contract drowning in cross-references is as bad as one long run-on clause.
  7. Changes are amendments. A behavior change must be legible as an amendment: the diff shows which clause changed and which case law (tests) now rules differently.
  8. Execution follows the succession of events. The boundary of every interaction is a performer with near-zero branching: it gathers the facts into a frozen evidence file (the only place the outside world is translated into the Definitions), consults the terms, and performs the decision — citing only the decision's own fields, never re-interpreting the terms. If performing needs more information, the decision's Definition is defective; fix it there.
  9. Operational demands are schedules. Caching, queuing, retries, timeouts, forms and screens — obligations about how performance happens are not scattered through the code as incantations. Each is declared as frozen data (a schedule, in the legal sense: an annex the clauses cite), and executed by a generic performer that holds no rules of its own. Any judgment a schedule requires — is this cached fact still fresh, should this delivery retry, is this field valid — is a pure function of the schedule and the facts, and gets case law like any other term.
  10. The docket is generated, never hand-written. The human-readable specification is produced from the case law (test names, grouped by clause), so it can never drift from behavior. Prose spec layers that do not execute are a second contract over the same subject matter — and two contracts is how disputes start.

Annex — the docket of schedules

The recurring schedules of principle 9. The membership test: can the obligation be stated as frozen data, and its judgment as a pure function?

  • C · Caching — how old gathered evidence may be and still be admissible: key, TTL, grace.
  • D · Deferred performance — what is owed later, and how surely: queue, attempts owed, backoff, escalation deadline.
  • F · Forms & screens — the offer a party fills in: fields, named validation rules, actions, explicit payload mapping.
  • P · Permissions — which party may invoke which clause; a check buried in a view is a hidden clause about who is party to the contract.
  • R · Rate limits — window, quota, scope.
  • G · Feature gates — audience, conditions, and a mandatory sunset date; a flag without one is a temporary amendment made permanent by neglect.
  • I · Idempotency — replay key, window, and whether a replay is served the prior receipt or rejected.
  • T · Retention & privacy — what data is kept, for how long, and its disposition after: keep, anonymize, purge.
  • S · Recurring performance — cadence, grace, and what a missed run owes.
  • O · Observability — which events must be recorded, and the thresholds whose breach raises an alert.
  • L · Copy & locale — every user-facing sentence a keyed, per-locale term; a missing key fails the build.
  • M · Records — how the Definitions persist. The in-memory Definitions are the contract; their stored form (table, columns, indexes) is a schedule, and every migration is an amendment paired with the Definition diff that motivated it.

Above the schedules sit the Articles — architecture as governing law: layering and who may import whom, deployable boundaries and what crosses them, one owner per fact of state. Declared as data, checked by pure rulings over the import graph, amended only through a recorded rationale (the ADR, kept in the repo as the Article's amendment record).


The contract, in Python

Definitions. Parties and terms, declared before use — nothing implied:

from dataclasses import dataclass
from datetime import date, timedelta
from enum import Enum


class Party(Enum):
    CUSTOMER = "customer"
    MERCHANT = "merchant"
    PROCESSOR = "payment_processor"


class DenialReason(Enum):
    WINDOW_CLOSED = "refund window closed"
    ORDER_NOT_SETTLED = "order not yet settled"


@dataclass(frozen=True)
class Order:
    total_cents: int
    settled: bool
    delivered_on: date | None
    damaged_on_arrival: bool


@dataclass(frozen=True)
class RefundRequest:
    requested_on: date


@dataclass(frozen=True)
class RefundPolicy:
    window: timedelta = timedelta(days=14)
    restocking_fee_percent: int = 10


@dataclass(frozen=True)
class RefundDecision:
    approved: bool
    amount_cents: int = 0
    paid_by: Party | None = None
    reason: DenialReason | None = None

The operative clause. One interaction, one main function. Near-zero cyclomatic complexity; every line is an obligation with a readable name. It states who owes what, when — and nothing about how:

def settle_refund_request(
    request: RefundRequest, order: Order, policy: RefundPolicy
) -> RefundDecision:
    """Clause 4 — Refunds. Pure: facts in, decision out."""
    if not order_is_refundable(order):
        return RefundDecision(approved=False, reason=DenialReason.ORDER_NOT_SETTLED)
    if not refund_window_is_open(request, order, policy):
        return RefundDecision(approved=False, reason=DenialReason.WINDOW_CLOSED)
    return RefundDecision(
        approved=True,
        amount_cents=refundable_amount(order, policy),
        paid_by=liable_party(order),
    )

The internals. Same principle, one level down — but not abused. These bodies are already plainer than further names would be, so the splitting stops here:

def order_is_refundable(order: Order) -> bool:
    """Clause 4.1 — only settled, delivered orders may be refunded."""
    return order.settled and order.delivered_on is not None


def refund_window_is_open(
    request: RefundRequest, order: Order, policy: RefundPolicy
) -> bool:
    """Clause 4.2 — the window runs from delivery, not purchase."""
    return request.requested_on <= order.delivered_on + policy.window


def refundable_amount(order: Order, policy: RefundPolicy) -> int:
    """Clause 4.3 — goods damaged on arrival refund in full;
    otherwise the merchant withholds the restocking fee."""
    if order.damaged_on_arrival:
        return order.total_cents
    fee = order.total_cents * policy.restocking_fee_percent // 100
    return order.total_cents - fee


def liable_party(order: Order) -> Party:
    """Clause 4.4 — the merchant bears damage; the processor bears the rest."""
    return Party.MERCHANT if order.damaged_on_arrival else Party.PROCESSOR

Case law. Pure tests against the top-level clause. Each name is a claim a party could bring; no mocks, no setup rituals — because the terms are pure. These tests pin the contract, so the internals can be redrafted freely as long as the rulings stand:

def test_customer_may_return_damaged_goods_for_a_full_refund():
    order = Order(total_cents=10_000, settled=True,
                  delivered_on=date(2026, 7, 1), damaged_on_arrival=True)
    decision = settle_refund_request(
        RefundRequest(requested_on=date(2026, 7, 5)), order, RefundPolicy()
    )
    assert decision.approved
    assert decision.amount_cents == 10_000
    assert decision.paid_by is Party.MERCHANT


def test_undamaged_returns_bear_the_restocking_fee():
    order = Order(total_cents=10_000, settled=True,
                  delivered_on=date(2026, 7, 1), damaged_on_arrival=False)
    decision = settle_refund_request(
        RefundRequest(requested_on=date(2026, 7, 5)), order, RefundPolicy()
    )
    assert decision.approved and decision.amount_cents == 9_000


def test_the_window_runs_from_delivery_and_then_closes():
    order = Order(total_cents=10_000, settled=True,
                  delivered_on=date(2026, 7, 1), damaged_on_arrival=False)
    late = RefundRequest(requested_on=date(2026, 7, 16))
    decision = settle_refund_request(late, order, RefundPolicy())
    assert not decision.approved
    assert decision.reason is DenialReason.WINDOW_CLOSED

Execution of the contract. Side effects live at the boundary, after the decision. The performer gathers facts (impure), consults the terms (pure), then performs. It never re-interprets the terms:

def handle_refund_endpoint(order_id: str, request: RefundRequest) -> None:
    order = orders_repo.load(order_id)                    # gather facts
    decision = settle_refund_request(request, order, current_policy())  # consult terms
    if decision.approved:                                  # perform
        payments.credit(order_id, decision.amount_cents, from_=decision.paid_by)
    notifications.send_refund_outcome(order_id, decision)

The dispute test

When you cannot decide whether code honors this manifesto, ask the contract questions:

  • Could each party point to the clause that binds them? If a stakeholder asks "when do we charge the restocking fee?", is the answer one named function — or an archaeology dig through branches?
  • Could a judge rule from the document alone? If deciding what the code should do requires running it against production state, terms and execution are entangled.
  • Would an amendment be legible? If waiving the restocking fee for damaged goods touches ten files, the contract was drafted badly.
  • Is a clause hiding? An inline condition nobody can name is a term nobody agreed to.

And the counter-test, so the principle is not abused: does this function exist to name a rule, or only to make a metric happy? A helper that no party would ever cite in a dispute is not a clause — inline it.

Code that passes these tests can be reviewed clause by clause, tested claim by claim, and amended without litigation. That is the whole idea: draft your code the way a careful lawyer drafts a contract — so that every party, human or machine, can rely on it.


Part II — The convention: Case-Law Testing (CaC/CL-1)

Principle 5 hardened into enforceable convention. This is also the answer to Gherkin: the specification layer BDD tools externalize into prose is internalized here — the operative clause is the readable spec, the case law is the executable scenarios, and the docket is the generated stakeholder view. Gherkin needs three artifacts per rule (.feature prose that cannot execute, regex step glue, real code) and the prose can silently drift; case law needs one artifact that fails the build if it lies.

Seven rules, each mechanically checkable:

  1. One file per clause. case_law/test_clause_4_refunds.py pins Clause 4 and nothing else. The docket mirrors the contract's table of contents.
  2. The test name is a claim a party could bring, written as a full sentence in snake_case: test_customer_may_return_damaged_goods_for_a_full_refund. If it cannot be read aloud in a meeting, it fails review. (Lintable: length ≥ 6 words, contains a party noun.)
  3. Facts / Claim / Ruling structure, in that order, comment-marked. Given/When/Then without the DSL: facts are constructed from the Definitions, the claim is one call to the operative clause, the ruling is plain asserts.
  4. Sue at the highest court. Case law calls only top-level clauses, never the internals. Internals stay redraftable as long as rulings stand. (Lintable: an import allowlist per case-law file.)
  5. No mocks in case law. A mock in a case-law test is proof the terms are not pure — fix the contract, not the test. Mocks are permitted only in thin execution-layer tests at the boundary.
  6. Precedent is append-mostly. Amending a clause may change a ruling, and the diff must show both together: the clause edit and the case-law edit in one commit. Deleting a case without amending a clause is overruling without authority — blocked in review.
  7. The docket is the stakeholder document. Generate the human-readable spec from the case law (pytest --collect-only -q case_law/), never the other way around. Gherkin's one genuine benefit, recovered at zero drift.

A case-law test, in full:

# case_law/test_clause_4_refunds.py
def test_customer_may_return_damaged_goods_for_a_full_refund():
    # Facts — the Definitions, cited by type, not by regex
    order = Order(total_cents=10_000, settled=True,
                  delivered_on=date(2026, 7, 1), damaged_on_arrival=True)

    # Claim — settled by the operative clause, nothing lower
    decision = settle_refund_request(
        RefundRequest(requested_on=date(2026, 7, 5)), order, RefundPolicy())

    # Ruling
    assert decision.approved
    assert decision.amount_cents == 10_000
    assert decision.paid_by is Party.MERCHANT

Two more dispute-test questions, for case law:

  • Does every claim have standing? A test no party would ever cite is not precedent — it is coverage theater. Delete it or promote it.
  • Could the docket brief a stakeholder? If the printed test names, read as sentences, would not survive a product meeting, the naming discipline has lapsed.

Part III — The full cycle (principle 8, worked)

The endpoint is the performer: gather → consult → perform, near-zero branching. It contains no business rule — not even if decision.approved; whether an approval pays out is a term of performance and lives in step 3.

# app/refunds/endpoint.py — the performer. Impure, and proud of it.
@app.post("/orders/{order_id}/refund-requests")
def handle_refund_request(order_id: OrderId, body: RefundRequestBody) -> RefundOutcomeBody:
    facts = gather_refund_facts(order_id, body)             # step 1 — impure reads
    decision = settle_refund_request(                       # step 2 — pure terms
        facts.request, facts.order, facts.policy)
    receipt = perform_refund_decision(order_id, decision)   # step 3 — impure writes
    return RefundOutcomeBody.from_decision(decision, receipt)

Fact-gathering returns a named, frozen evidence file. Translation from the outside world (HTTP body, DB row) into the contract's types happens here and only here — the terms never see a raw dict or ORM object:

# app/refunds/facts.py — evidence gathering. Impure reads, no rulings.
@dataclass(frozen=True)
class RefundFacts:
    """The evidence file: every Definition Clause 4 will cite."""
    request: RefundRequest
    order: Order
    policy: RefundPolicy


def gather_refund_facts(order_id: OrderId, body: RefundRequestBody) -> RefundFacts:
    return RefundFacts(
        request=body.as_refund_request(),      # HTTP → Definitions
        order=orders_repo.load(order_id),      # DB row → Order
        policy=policies_repo.current(),        # config → RefundPolicy
    )

Performance mirrors the decision it executes — it cites only the decision's own fields and never reaches back into Order to re-derive what the terms already settled. If performance needs more information, the decision's Definition is defective:

# app/refunds/perform.py — performance of the contract.
def perform_refund_decision(order_id: OrderId, decision: RefundDecision) -> Receipt:
    """Execute Clause 4's decision at the boundary."""
    if decision.approved:
        receipt = payments.credit(order_id, decision.amount_cents,
                                  from_=decision.paid_by)
    else:
        receipt = Receipt.nothing_owed(reason=decision.reason)
    notifications.send_refund_outcome(order_id, decision)
    audit_log.record(order_id, decision, receipt)
    return receipt

Testing policy per layer: the clause gets the full docket; gathering and performing get thin execution-layer tests where mocks are permitted (rule 5); the endpoint needs at most one wiring test — with three lines and no branches there is almost nothing left to get wrong.


Part IV — Schedules, worked (principle 9)

Every internal concern decomposes into the same three parts:

  1. Terms — a frozen dataclass (or as const object) declaring the obligation, living next to the Definitions, cited by name.
  2. Rulings — every judgment the terms require is a pure function of the terms and the facts. This is where the case law attaches.
  3. A generic performer — one piece of machinery per concern, written once, holding zero business decisions.

The test for whether you have done it right: could you change the obligation by editing only the schedule? Shorten a TTL, add a retry, make a field optional — each must be a one-line amendment to declared data, visible in the diff as exactly that.

Schedule C — Caching (evidence freshness)

Caching is a rule about how old evidence may be and still be admissible.

@dataclass(frozen=True)
class CacheTerms:
    """How old a gathered fact may be and still be admissible."""
    key: str
    ttl: timedelta
    grace: timedelta = timedelta(0)   # stale-while-revalidate allowance

EXCHANGE_RATE = CacheTerms(key="fx:{base}:{quote}",
                           ttl=timedelta(minutes=15), grace=timedelta(minutes=2))


class Admissibility(Enum):
    FRESH = "fresh"; STALE_IN_GRACE = "grace"; EXPIRED = "expired"

def admissibility(fetched_at: datetime, now: datetime, terms: CacheTerms) -> Admissibility:
    age = now - fetched_at
    if age <= terms.ttl:               return Admissibility.FRESH
    if age <= terms.ttl + terms.grace: return Admissibility.STALE_IN_GRACE
    return Admissibility.EXPIRED


def test_a_rate_older_than_its_ttl_but_in_grace_is_served_while_refetching():
    fetched = datetime(2026, 8, 6, 12, 0)
    now = datetime(2026, 8, 6, 12, 16)   # 16 min: past TTL, inside grace
    assert admissibility(fetched, now, EXCHANGE_RATE) is Admissibility.STALE_IN_GRACE

The generic performer (@cached(EXCHANGE_RATE) wrapping a fact-gatherer) reads the terms and consults the ruling; it holds no TTLs, no keys, no policy. Swapping Redis for memcache touches machinery and no schedule.

Schedule D — Deferred performance (async / queues)

A queued task is performance that need not be immediate but must be certain. Grep DeliveryTerms and you hold the complete inventory of what the system owes later.

@dataclass(frozen=True)
class DeliveryTerms:
    """Performance need not be immediate, but must be certain."""
    queue: str
    attempts_owed: int
    backoff_base: timedelta
    escalate_after: timedelta       # breach: page a human

REFUND_OUTCOME_NOTICE = DeliveryTerms(queue="notifications", attempts_owed=5,
                                      backoff_base=timedelta(seconds=30),
                                      escalate_after=timedelta(hours=1))


def after_failed_attempt(attempt_no: int, first_tried: datetime,
                         now: datetime, terms: DeliveryTerms) -> Retry | Escalate | GiveUp:
    if now - first_tried >= terms.escalate_after:
        return Escalate(reason="delivery deadline breached")
    if attempt_no >= terms.attempts_owed:
        return Escalate(reason="attempts exhausted")
    return Retry(at=now + terms.backoff_base * 2 ** (attempt_no - 1))


def test_a_breached_deadline_escalates_even_with_attempts_remaining():
    first = datetime(2026, 8, 6, 12, 0)
    now = datetime(2026, 8, 6, 13, 1)     # 61 min, attempt 2 of 5
    assert isinstance(after_failed_attempt(2, first, now, REFUND_OUTCOME_NOTICE), Escalate)

The performer defers with one call, citing the schedule by name: defer(notifications.send_refund_outcome, (order_id, decision), terms=REFUND_OUTCOME_NOTICE). The dispatcher consults the ruling after every failure; it decides nothing itself.

Schedule F — Forms & screens (frontend)

A form is an offer: what facts a party must tender, in what shape, and what happens on tender. Fields, named validation rules, buttons, endpoint and explicit payload mapping — one typed declaration. The React component that renders it is a generic performer that can draw any form and knows none.

// schedules/refundRequestForm.ts — Schedule F-1. The offer, declared.
export const refundRequestForm = {
  clause: "4",
  fields: [
    { name: "requestedOn", label: "Date of request", kind: "date",
      required: true, rules: [requestedOnMayNotBeInTheFuture] },
    { name: "reason", label: "Why are you returning this?", kind: "select",
      required: true, options: ["damaged_on_arrival", "changed_mind"], rules: [] },
  ],
  actions: [
    { label: "Request refund", kind: "submit",
      call: { method: "POST", endpoint: "/orders/{orderId}/refund-requests",
              payload: (v: Values) => ({
                requested_on: v.requestedOn,
                damaged_on_arrival: v.reason === "damaged_on_arrival" }) } },
    { label: "Cancel", kind: "dismiss" },
  ],
} satisfies FormTerms<Values>;

// Named, pure validation rule — case law runs in vitest with no DOM:
export function requestedOnMayNotBeInTheFuture(value: string, today: string): Grievance | null {
  return value > today
    ? { field: "requestedOn", says: "The request date cannot be in the future." }
    : null;
}

test("the form tenders exactly what Clause 4's endpoint is owed", () => {
  const body = refundRequestForm.actions[0].call.payload({
    requestedOn: "2026-08-05", reason: "damaged_on_arrival" });
  expect(body).toEqual({ requested_on: "2026-08-05", damaged_on_arrival: true });
});

<ContractForm terms={refundRequestForm} /> walks the terms, runs the rules, disables submit while grievances stand, performs the declared call. Adding a field is a diff in Schedule F-1 only.

Schedule P — Permissions (standing)

Which roles are party to an interaction at all. An authorization check buried in a view is a hidden clause about who is party to the contract.

@dataclass(frozen=True)
class InvocationTerms:
    clause: str
    parties: frozenset[Role]

REQUEST_REFUND = InvocationTerms(clause="4",
                                 parties=frozenset({Role.CUSTOMER, Role.SUPPORT}))

def may_invoke(actor: Actor, terms: InvocationTerms) -> Allowed | Denied:
    if actor.role in terms.parties:
        return Allowed()
    return Denied(reason=f"{actor.role.value} is not party to clause {terms.clause}")

def test_a_courier_has_no_standing_to_request_a_refund():
    assert isinstance(may_invoke(Actor(role=Role.COURIER), REQUEST_REFUND), Denied)

Schedule R — Rate limits

The quota lives with the clause it protects, not in infrastructure config where no stakeholder will find it.

@dataclass(frozen=True)
class QuotaTerms:
    scope: str
    window: timedelta
    invocations_owed: int

REFUND_REQUESTS = QuotaTerms(scope="per_customer",
                             window=timedelta(days=1), invocations_owed=3)

def over_quota(history: list[datetime], now: datetime, terms: QuotaTerms) -> bool:
    return sum(1 for t in history if now - t < terms.window) >= terms.invocations_owed

def test_the_fourth_refund_request_in_a_day_is_refused():
    noon = datetime(2026, 8, 6, 12)
    assert over_quota([noon - timedelta(hours=h) for h in (1, 2, 3)], noon, REFUND_REQUESTS)

Schedule G — Feature gates

Every flag carries its own funeral: the sunset date is a required term, and a lint rule fails the build on any gate past its date — flags cannot rot into permanent temporary amendments.

@dataclass(frozen=True)
class GateTerms:
    name: str
    audience: str          # cohort that may pass while the gate stands
    sunset: date           # after this, closed — and lint flags it

INSTANT_REFUNDS = GateTerms("instant_refunds",
                            audience="beta_customers", sunset=date(2026, 10, 1))

def gate_is_open(actor: Actor, today: date, terms: GateTerms) -> bool:
    return today <= terms.sunset and terms.audience in actor.cohorts

def test_a_gate_past_its_sunset_is_closed_even_for_its_audience():
    beta = Actor(role=Role.CUSTOMER, cohorts=frozenset({"beta_customers"}))
    assert not gate_is_open(beta, date(2026, 10, 2), INSTANT_REFUNDS)

Schedule I — Idempotency

Within the window, the same tender is the same tender, and it is owed the original receipt — not a second performance.

@dataclass(frozen=True)
class ReplayTerms:
    key: str
    window: timedelta

REFUND_SUBMISSION = ReplayTerms(key="refund:{order_id}", window=timedelta(hours=24))

def on_replay(prior: Receipt | None, age: timedelta,
              terms: ReplayTerms) -> ServePrior | Proceed:
    if prior is not None and age <= terms.window:
        return ServePrior(prior)
    return Proceed()

def test_resubmitting_within_the_window_returns_the_original_receipt():
    prior = Receipt(id="r-1")
    assert on_replay(prior, timedelta(minutes=5), REFUND_SUBMISSION) == ServePrior(prior)

Schedule T — Retention & privacy

Declared when the data is first collected, not negotiated during the audit. The sweeper job (Schedule S) consults this ruling; it decides nothing itself.

class Disposition(Enum):
    KEEP = "keep"; ANONYMIZE = "anonymize"; PURGE = "purge"

@dataclass(frozen=True)
class RetentionTerms:
    record: str
    keep_for: timedelta
    then: Disposition

REFUND_DECISIONS = RetentionTerms("refund_decisions",
                                  keep_for=timedelta(days=7 * 365),
                                  then=Disposition.ANONYMIZE)

def disposition(age: timedelta, terms: RetentionTerms) -> Disposition:
    return terms.then if age > terms.keep_for else Disposition.KEEP

def test_a_decision_older_than_seven_years_is_anonymized_not_purged():
    assert disposition(timedelta(days=7 * 365 + 1), REFUND_DECISIONS) is Disposition.ANONYMIZE

Schedule S — Recurring performance (cron)

Cron expressions say when; they never say what lateness means. The schedule declares the grace and the obligation a miss creates.

@dataclass(frozen=True)
class CadenceTerms:
    task: str
    every: timedelta
    grace: timedelta

RECONCILE_LEDGER = CadenceTerms("reconcile_ledger",
                                every=timedelta(days=1), grace=timedelta(hours=2))

def standing(last_run: datetime, now: datetime,
             terms: CadenceTerms) -> Current | Due | Missed:
    since = now - last_run
    if since < terms.every:                return Current()
    if since < terms.every + terms.grace:  return Due()
    return Missed(overdue_by=since - terms.every)

def test_a_run_later_than_cadence_plus_grace_is_a_breach():
    last = datetime(2026, 8, 5, 3, 0)
    now = datetime(2026, 8, 6, 5, 1)     # 26h01m: past the 2h grace
    assert isinstance(standing(last, now, RECONCILE_LEDGER), Missed)

Schedule O — Observability (the duty to notice)

Which measurements the program is obliged to keep, and the thresholds whose breach must wake someone. The threshold stops living in a Grafana panel nobody reviews.

@dataclass(frozen=True)
class VigilanceTerms:
    measure: str
    threshold: float
    over: timedelta

REFUND_ESCALATIONS = VigilanceTerms(measure="refund_escalations",
                                    threshold=5, over=timedelta(hours=1))

def breach(count_in_window: int, terms: VigilanceTerms) -> Alert | None:
    if count_in_window > terms.threshold:
        return Alert(says=f"{terms.measure}: {count_in_window} in {terms.over}, "
                          f"threshold {terms.threshold}")
    return None

def test_six_escalations_in_an_hour_raises_an_alert():
    assert breach(6, REFUND_ESCALATIONS) is not None

Schedule L — Copy & locale

User-facing prose is the part of the contract the customer actually reads. The case law is exhaustive over the Definitions, so a new denial reason without copy fails the build in every locale at once.

COPY: dict[str, dict[str, str]] = {
    "refund.denied.window_closed": {
        "en": "The return window for this order has closed.",
        "es": "El plazo de devolución de este pedido ha finalizado.",
    },
    "refund.denied.order_not_settled": {
        "en": "This order has not settled yet — refunds open once it does.",
        "es": "Este pedido aún no se ha liquidado; podrás pedir el reembolso después.",
    },
}

def say(key: str, locale: str) -> str:
    return COPY[key][locale]          # a missing key raises — by design

def test_every_denial_reason_has_copy_in_every_locale():
    for reason in DenialReason:
        for locale in ("en", "es"):
            assert say(f"refund.denied.{reason.name.lower()}", locale)

Schedule M — Records (data models)

The distinction that matters: the in-memory shapes (Order, RefundDecision) are not a schedule — they are the Definitions, the contract itself (principle 1). What is a schedule is how a Definition persists: table, columns, indexes, and how the stored form changes over time. Confuse the two and the ORM class becomes the contract, coupling business terms to storage choices.

@dataclass(frozen=True)
class RecordTerms:
    """Binds one Definition to one stored form. Storage words live here only."""
    definition: type
    table: str
    columns: dict[str, str]        # field → column
    indexes: tuple[str, ...]
    owner: str                     # must agree with Article III

ORDER_RECORD = RecordTerms(
    definition=Order, table="orders",
    columns={"total_cents": "total_cents", "settled": "is_settled",
             "delivered_on": "delivered_on", "damaged_on_arrival": "damaged_on_arrival"},
    indexes=("delivered_on",), owner="storefront.db")


def uncovered_fields(terms: RecordTerms) -> list[str]:
    """Every field of the Definition must map to a column."""
    return [f.name for f in fields(terms.definition) if f.name not in terms.columns]

def undeclared_drift(live: TableSchema, terms: RecordTerms) -> list[str]:
    """The database may hold nothing the schedule does not declare."""
    declared = set(terms.columns.values())
    return [col for col in live.columns if col not in declared]


def test_every_field_of_order_has_a_declared_column():
    assert uncovered_fields(ORDER_RECORD) == []

def test_the_live_orders_table_holds_nothing_undeclared():
    live = scan_schema("orders")          # gathered fact (impure, conftest)
    assert undeclared_drift(live, ORDER_RECORD) == []

Migrations are three-part amendments. A migration is never a standalone SQL file: the diff must show the Definition amendment, the record-terms amendment, and the migration that performs it — in one commit, with the case law that motivated the change. CI rejects any one without the other two.

The counter-test for models: a column whose Definition field no clause ever cites is data collected without purpose — a term nobody agreed to. Schedule T will ask awkward questions about it; better to strike it.


Part V — Articles: architecture as governing law

Schedules govern one concern each. Architecture is the governing-law section — terms that bind every clause at once: which layers exist and who may import whom; what is synchronous and what deferred; where state may live; what is one deployable. Under CaC these are Articles: declared, enforced, amended only with a recorded rationale.

# articles/governing_law.py — binds every clause in the program.
@dataclass(frozen=True)
class LayeringTerms:
    """Article I — who may cite whom. Lower layers never import upward."""
    order: tuple[str, ...]

ARTICLE_I = LayeringTerms(order=(
    "machinery",     # generic performers: cache, queue, renderer
    "schedules",     # declared operational terms
    "clauses",       # pure business terms + Definitions
    "facts",         # evidence gathering (impure reads)
    "performers",    # endpoints, jobs, handlers (impure writes)
))


@dataclass(frozen=True)
class BoundaryTerms:
    """Article II — what is one deployable, and what crosses between them."""
    deployable: str
    may_call: tuple[str, ...]        # other deployables, by contract only
    crossing: str                    # "http" | "queue" — never a shared DB

ARTICLE_II = (BoundaryTerms("storefront", may_call=("payments",), crossing="http"),
              BoundaryTerms("payments",   may_call=(),            crossing="queue"))


@dataclass(frozen=True)
class StateTerms:
    """Article III — where truth lives. One owner per fact; all else is cache."""
    fact: str
    owner: str

ARTICLE_III = (StateTerms("orders", owner="storefront.db"),
               StateTerms("ledger", owner="payments.db"))

Articles are enforced, not aspirational — an import graph is just data, so the architecture check is ordinary case law run in CI (import-linter and friends are mere machinery):

def layering_violations(imports: ImportGraph, terms: LayeringTerms) -> list[Violation]:
    rank = {layer: i for i, layer in enumerate(terms.order)}
    return [Violation(edge) for edge in imports
            if rank[edge.importer_layer] < rank[edge.imported_layer]]


def test_no_clause_may_cite_a_performer():
    graph = scan_imports("src/")          # gathered fact (impure, in conftest)
    assert layering_violations(graph, ARTICLE_I) == []

ADRs are amendment records — in the repo, adjacent to the terms they amend. The diff that changes an Article must carry its recital (why), the operative change, and the case law it creates or overrules; CI blocks an Article edit without one:

# articles/amendments/0007_payments_becomes_async.md
Amendment 7 — to Article II (boundaries)

Recital (why): synchronous capture blocked checkout for the p99 of
processor latency; the parties accept eventual receipt in exchange
for a bounded checkout time.

Operative change: storefront → payments crossing amended
"http" → "queue", under Schedule D terms CAPTURE_PAYMENT
(attempts_owed=5, escalate_after=15min).

Case law affected:
  - test_checkout_completes_without_awaiting_capture  (new)
  - test_capture_breach_pages_the_merchant            (new)
  - test_checkout_returns_the_captured_receipt        (overruled)

The hierarchy, top to bottom: Articles bind everything; clauses bind one interaction; schedules bind one operational concern; machinery binds nothing — it obeys. Anything structural you cannot write as an Article with a pure violation check is a choice you have not actually made yet.


Part VI — Adoption order

  1. Move existing pure tests into case_law/, one file per clause; rename to claim sentences.
  2. Add the Facts / Claim / Ruling comments; delete any mock from a case-law file by purifying the term it was covering.
  3. Ship the lint plugin for convention rules 1–5 and the CI diff check for rule 6.
  4. Wire the docket command into CI output so every build prints the current case law — that artifact replaces any .feature file you would have written.
  5. Extract schedules as you touch them: each scattered TTL, retry decorator, permission check or hand-built form becomes a declared term with a pure ruling. Declare the Articles last, once the layers are visible — then let CI enforce them.

The closing dispute test for the internals: an internal concern is drafted correctly when the obligation is a schedule you could amend in one line, the judgment is a pure ruling with case law, and the machinery is a generic performer that would work unchanged for any other schedule of its kind. If changing a TTL, a retry count, or a form field touches machinery, the schedule has leaked; if the machinery makes a decision the ruling should have made, a term is hiding. Same dispute test, new territory.

Manifesto source file