Contracts

The movement core speaks seven contracts. Six are written down here with an integer version of its own, independent of the release tag and of the others, so an embedder, an adapter vendor or a second engine can say exactly what it speaks. The sixth, the language, is versioned by the release itself.

contractdocumentcurrentsupporteddeprecatedon the wire
catalog-snapshotcatalog-snapshot.md33nonecontractVersion on the snapshot
park-statepark-state.md41, 3, 4noneversion on the state
host-familieshost-families.md22nonehostFamiliesVersion on segment.open
adapter-protocoladapter-protocol.md22 (minors 0–8)noneprotocolVersion on every request and the manifest
language-versionslanguage-versions.mdthe releasethe release's compatibility key (0.15)nonelanguageVersion beside every program
system-callersystem-caller.md11nonenone: an in-process interface
story-analysisstory-analysis.md21, 21storyAnalysisVersion on the analysis

How a version moves #

The rule mirrors the language's (language-versions.md):

Each document #

Every contract document carries the same three things: its version on a line of its own at the top (Version: N), the JSON shapes as TypeScript-ish type blocks, and a "Changes" list, newest first.

Conformance kits #

A contract written down is a claim; a kit is how you check it. Each kit is data plus a runner, so it works against your implementation as well as ours.

The host kit: recorded engine runs #

A host recording is one entry into the engine as data: what it was handed, every request it made of its host in the order it made them, with the answer each got and the requests already answered when it was made, and how it ended. The format is the zod schema in packages/engine-contracts/conformance/recording.ts:

{
  kit: 'host';
  version: 3;                          // the recording format's own version (2 still reads)
  entry: 'run' | 'resume' | 'settleRace';
  input: RunInput | ResumeInput | SettleRaceInput;  // with its languageVersion
  methods: string[];                   // every host method the recorded host served
  requests: Array<
    { family: string; method: string; args: unknown; after: number[] } &
    ({ answer?: unknown } | { error: ThrownError })
  >;
  result?: unknown;                    // exactly one of result and failure
  failure?: ThrownError;
}

Replaying. An engine under replay is handed the recorded input and a host that answers from the recording. It conforms when:

A divergence over a request with no recorded counterpart also carries that request in full (unmatched on the verdict). The first divergence is named: the request with no counterpart and the nearest recorded one, the request reordered and the one it waited on, the recorded request never made, or the path where the results first differ. The rules are packages/engine-contracts/conformance/replay.ts.

Replaying a recording against your engine or host. A recording is one line of JSON, and each line replays on its own.

Recording your own host. Wrap any EngineHost with a recorder: it hands the engine and the host each other's own values, keeps JSON copies, and refuses a request or answer JSON cannot carry, and any request made after the entry ended. The recording and replay rules are packages/engine-contracts/conformance/.

What proves the host contract here. The language's proof is the engine package's shim tests (the Rust engine run through its shim against a host, packages/movement-engine), apps/cli's slot conformance (slot_conformance.test.ts, every slot module held to the same cases over the wire) and its run-host contract (run_host_contract.test.ts, the host listen-fire itself provides). The recorded-run harness, with its corpora, its HTTP host double and its re-harvest command, is a host's own proof and belongs to the host that records them: lf-platform has one, under apps/api/src/services/movement_engine/conformance/.

The language kit: checked programs #

Every program the test suites check, with its catalog snapshot, language version and full diagnostics. The corpus and its tools are lf-platform's: in an lf-platform checkout, pnpm language:conformance <checks.json> replays a corpus against the checker, ours or your own, and pnpm checks:corpus assembles ours into target/checks/checks.json, because the repository pins each program by a hash of its text and the text lives in its test. The tools' guide is in lf-platform, packages/legacy-lang/conformance/README.md.

The adapter kit #

A vendor's server is run through the adapter protocol with listen-fire itself (listen-fire check, listen-fire catalog, listen-fire run --webhook, listen-fire serve) against undefined: see Build your first adapter.