Contracts
The authored surface of OpenProse -- Markdown contracts, the five kinds, and the load-bearing sections that turn a declaration into a typed, subscribable node.
Contracts
A contract is the thing you author. It is a Markdown file -- *.prose.md --
with a small YAML frontmatter and a handful of ### sections. The Markdown,
with its durable trail, is the public artifact: it can leave for any
Prose-Complete harness with no lost semantics. The deployment's secrets and
data stay private.
Authors are co-equal. A human and an agent write contracts the same way, read the same sections, and fork or compose each other's work without translation. Every section below is designed for both readers from the start.
This page is the authored surface only -- what you write. The runtime that serves these contracts (fingerprints, memoization, the continuity clock, receipts) is the Reactor harness's concern; see the Reactor section for that. Here, intent lives only in the contract.
Frontmatter and identity
A contract opens with YAML frontmatter. Two fields carry weight:
kind:-- which of the five contract kinds this is (below). This is the one field that changes how the file is executed.name:-- the human-facing slug.
A third field, id:, is a durable identity (a base32 token) minted once by
tooling and preserved across name: and filename renames. You do not
hand-write id:; tooling manages it. name: is what you read; id: is what
keys the node's world-model and receipt-ledger state on disk.
The five kinds
kind: selects one of five authored kinds. Each has a different run model.
| Kind | Run model | Purpose |
|---|---|---|
responsibility | Served (continuously reconciled) | The headline kind: a mounted DAG node maintaining a standing truth over time. |
function | Called (one-shot) | A stateless, ephemeral helper. Bind ### Parameters, run one render, return ### Returns. No Forme phase, no world-model. |
gateway | Mounted as external-driven | Sugar for an external-driven responsibility. Compiles into a trigger registration for reactor serve; refuses a direct run. |
test | Via the test path | Fixtures plus natural-language assertions against a subject responsibility or function. |
pattern | Instantiated at compile time | A reusable coordination algorithm, expanded into nodes at compile time. Never directly run. |
There is no kind: service (it was renamed to function) and no
kind: system (deleted). Cross-node composition is a Forme-wired
subscription between responsibilities; intra-node composition is an imperative
call inside one render. There is never an internally-autowired graph kind.
The headline kind is responsibility. The rest of this page teaches its
load-bearing sections, because those are what make a contract a typed,
subscribable node rather than a bare prompt.
The load-bearing sections
Most ### sections mean exactly what their name says. ### Description is a
human summary preserved for readers, not a contract. ### Goal is the render's
one-sentence standing intent. These defer to the obvious.
Three sections carry the semantic load of a responsibility, and the contract
must state them rather than defer: ### Requires, ### Maintains, and
### Continuity.
### Maintains -- the world-model schema (four jobs at once)
### Maintains is the schema for the truth this node keeps current. It does
four jobs in one section:
- It types the maintained truth -- the shape of the world-model the node owns.
- It carries the canonicalization spec -- which fields are material and how they normalize. This compiles into the node's fingerprint, the cheap content identity the run phase compares.
- It declares facets (below) -- the named parts of the truth.
- It states postconditions -- the obligations a render must satisfy before it may commit.
There is no separate judge and no ### Criteria section. Satisfaction folds
into ### Maintains: checked deterministically where it can be written as a
validator, and self-attested by the render where it is semantic. A render that
cannot satisfy its postconditions commits nothing -- the prior truth stands,
and a failed receipt records why.
Facets make subscription structural
A #### sub-heading inside ### Maintains declares a facet -- a named
part of the truth. The facet's name is, at once, three things:
- its fingerprint unit (the canonicalizer emits one token per facet, plus an always-on atomic token over the whole truth);
- its subscription symbol (a consumer names it in
### Requires, and the reconciler wakes that consumer only when that facet's token moves --Requires.<facet>↔Maintains.<facet>); - and its region of the world-model.
Declaring no parts is the atomic default -- one truth, one token -- and costs
nothing. This adds no new grammar: it reuses the heading hierarchy and the
Requires↔Maintains join. Structure is subscription. The way you shape the
### Maintains section is the way downstream nodes subscribe to it.
### Requires -- what the node subscribes to
### Requires names the upstream facets this node consumes. Forme matches each
entry to a producer's ### Maintains facet and draws the subscription edge.
### Requires is the input side of the memo key: the run phase fingerprints
the node's contract together with its subscribed inputs, and re-renders only
when one of them moves.
### Continuity -- the wake source
### Continuity declares when, beyond a subscribed input changing, a node
should re-render. It is not a schedule. A node is input-driven by default
(it wakes when a subscribed facet moves). ### Continuity adds one of:
- a self-driven cadence -- the truth goes stale on its own. A
valid_untilfreshness state lives in the world-model as data;### Continuitycarries only the policy that reads it on a forecast cadence. When avalid_untillapses, the harness mechanically moves the facet's fingerprint and wakes the node -- no model call to decide that time has passed. - external-driven -- marks a
kind: gatewayso an ingress event wakes it.
The author writes these sections. The harness owns the fingerprinting, the forecast cadence, the receipts, and the subscription wiring. You declare the truth; the runtime decides, deterministically, when it is worth recomputing.
Functions: the call interface
A kind: function does not maintain a world-model and is not part of the DAG.
It declares a plain call interface instead of the four-jobs schema:
### Parameters-- the inputs bound at call time.### Returns-- the return value of its single render.
A lone function has no Forme phase: bind parameters, spawn one render, return
### Returns. It is the stateless, ephemeral helper.
A contract in full
Here is a real, migrated responsibility. Read it top to bottom: the >
description states intent for a human, ### Requires names the single facet it
subscribes to, ### Maintains types the CountSummary truth and declares one
#### structured facet with an explicit postcondition, and ### Continuity
declares it input-driven -- so it wakes only when its counts input moves.
---
name: count-summary
kind: responsibility
---
# Count Summary
> The single responsibility that turns the gateway's `counts` facet into a
> threshold-aware summary (U02). It reads its prior world-model by reference,
> writes a new `CountSummary`, and signs a receipt naming the upstream receipt it
> consumed. It skips when `counts` has not moved (U03).
### Requires
- `counts`: the numeric tallies. *(Maintained by `counter-events.counts`.)*
This is the only subscribed input. `count-summary` is **input-driven**: it wakes
iff the `counts` facet fingerprint moves. A metadata-only event moves only
`raw_events`, so this node stays dark while `Raw Event Auditor` wakes (U05).
### Maintains
The `CountSummary` world-model — the structured summary the alerting chain reads.
- `total` — the material event count.
- `by_kind` — the per-kind tallies.
- `threshold_crossed` — whether `total` reached the alert threshold.
- `explanation` — a short rationale string.
#### structured
The whole summary is material: any change to `total`, `by_kind`, or
`threshold_crossed` moves this node's truth and propagates to `Alert State`.
**Postcondition:** `total` equals the count of accepted material events;
`threshold_crossed` is true iff `total ≥ threshold`. Self-policed before signing —
no separate judge beat.
### Execution
Read the `counts` facet and the prior summary by reference, fold the per-kind
totals, set `threshold_crossed`, and commit the structured summary.
### Continuity
input-driven
And here is the gateway that feeds it. A gateway has no ### Requires (its
input arrives from outside the graph). Notice how ### Maintains splits the
ledger into two #### facets -- counts and raw_events -- so a
metadata-only event moves raw_events (waking the auditor) without moving
counts (the summary above stays dark). That split is the subscription wiring.
---
name: counter-events
kind: gateway
---
# Counter Events
> The gateway — the system's ingress. It has no `### Requires` (its input arrives
> from outside the graph), it `### Maintains` the canonical `CounterEventLedger`,
> and its `### Continuity` is **external-driven**, which is how Forme registers it
> as the single DAG entry point (U11).
### Continuity
external-driven
A webhook, a poll, or a manual kick becomes one external wake at the system's
edge. The gateway folds each accepted counter event into the canonical ledger and
projects two **independent facets** so a downstream subscriber wakes only on the
slice it actually depends on (U05). Replaying the same event id is a no-op — the
ledger dedups by id, so a re-delivery produces a byte-identical world-model and
the gateway memo-skips (U01/U03).
### Receives
- A counter event: `{ id, kind, value, material? }`. An event with
`material: false` is **accepted into the audit trail but excluded from the
tallies** — it is the metadata-only event that moves `raw_events` without moving
`counts`.
### Maintains
The `CounterEventLedger` — the standing truth every downstream responsibility
subscribes to. Its canonicalization splits the truth into the two facets below, so
a change to one slice never spuriously wakes a subscriber of the other.
- `high_water_mark` — the running material event total.
- `counts_by_kind` — the per-kind material tallies.
- `accepted_event_ids` — the full accepted id set (material and metadata-only).
- `last_seen_at` — an immaterial monotone marker (it never appears in a facet, so
it cannot wake a subscriber on its own).
#### counts
The numeric tallies (`high_water_mark`, `counts_by_kind`) over **material events
only**. Moves when a material event is accepted; does NOT move on a metadata-only
event. `Count Summary` and `Count Trend` subscribe here.
#### raw_events
The accepted-event id set plus duplicate / malformed flags. Moves whenever the
accepted set changes — including a metadata-only event. `Raw Event Auditor`
subscribes here.
### Emits
- count-summary
- raw-event-auditor
- count-trend
Forme keys the wake on the producing node; the subscribers above resolve their
edges to this gateway's `counts` / `raw_events` facets.
### Continuity recheck
A weekday 09:00 self-kick may re-scan even when no webhook fires; a byte-identical
re-scan memo-skips, so the self-kick costs nothing when nothing changed.
A bare prompt is any: it tells an agent what to do once and forgets. A
contract is a typed function over the world -- a declared truth with a fingerprint,
named facets others can subscribe to, and postconditions it must satisfy before
it commits. That is the whole difference, and it is authored entirely in
Markdown.
Where to go next
ProseScript
The optional imperative pinning layer inside ### Execution, for when declarative defaults are not enough.
World-model and fingerprints
How the Reactor harness turns a ### Maintains schema into a canonicalizer, fingerprints of meaning, and facet-granular wakeups.
Declare outcomes
The paradigm under the contract: why you declare a standing truth instead of a sequence of instructions.
Set up a project
Where contracts live on disk, and the ordered path from a keyless proof to a running reactor.
For the full language semantics, the authoritative source is the OpenProse
spec (01-Language.md)
and the loaded open-prose SKILL. If the docs and the skill ever disagree,
trust the skill.