# RLM Development Contract Declaration
**Status:** pre-freeze, advisory.
**Schema:** `lifeloop.v0.1`.
**Date:** 2026-05-08.
## Summary
Recursive Language Models (RLM) may begin development against
`lifeloop.v0.1` with the pre-freeze caveats below. This note exists so RLM
can prototype before the `lifeloop.v1` schema-lifecycle gate closes,
without inviting RLM-shaped semantics into Lifeloop core.
## Pre-freeze caveats
- `lifeloop.v0.x` is the **pre-freeze** development line. Breaking changes
may land between `v0.x` releases. Each break ships with a tombstone in
`docs/tombstones/` documenting migration. RLM should pin the exact
`v0.x` minor it consumes and expect to follow tombstones until `v1`.
- `lifeloop.v1` is frozen only after the schema-lifecycle gate in
`docs/specs/lifecycle-contract/body.md` is satisfied: CCD plus one non-CCD
client class consuming the same contract, plus the required conformance
adapter set.
- This declaration is **not** a stability promise for `v1`. It is a
shipping target for prototype work that wants to validate the
contract's shape against recursive-inference workloads before freeze.
## Contract surface RLM may rely on
The following fields and shapes are part of `lifeloop.v0.1` and define the
surface RLM prototype work can be built against. Field paths point to the
Rust types in `src/lib.rs` and the spec sections in
`docs/specs/lifecycle-contract/body.md`.
### Callback envelopes
- `CallbackRequest` carries `schema_version` (`"lifeloop.v0.1"` for this
pre-freeze line), `event` (a stable lifecycle-event wire name),
`event_id`, `adapter_id`, `adapter_version`, `integration_mode`,
`invocation_id`, optional `harness_session_id`, `harness_run_id`,
`harness_task_id`, optional `frame_context`, optional
`capability_snapshot_ref`, `payload_refs[]`, optional `sequence`, and
optional `idempotency_key`.
- `CallbackResponse` carries `status`, `client_payloads[]`, `receipt_refs[]`,
`warnings[]`, `failure_class`, and `retry_class`.
### Frame context (recursive frames)
`FrameContext` validates the recursive-frame model RLM cares about most:
- `frame_id` is required.
- `frame_class` is one of `top_level` or `subcall`.
- `frame_class = subcall` **requires** `parent_frame_id`; `top_level`
forbids it.
- `frame.opening` / `frame.opened` / `frame.ending` / `frame.ended` events
require `frame_context` to be present.
A recursive client may create nested frames under the same session by
emitting child `frame.opening` events with `frame_class = subcall` and
`parent_frame_id` set to the enclosing frame.
### Receipts
`LifecycleReceipt` carries lifeloop-run correlation through the
`(client_id, adapter_id, invocation_id)` tuple — there is no separate
`lifeloop_run_id` field; that triple is the run identifier. Receipts
also carry the always-present `sequence` and `parent_receipt_id`
nullable fields, plus `payload_receipts[]`, `warnings[]`,
`failure_class`, and `retry_class`. Receipts validate the
`receipt.emitted` non-recursion rule from the spec.
### Capability negotiation shape
The `RequirementLevel` (`required` / `preferred` / `optional`) and
`NegotiationOutcome` (`satisfied` / `degraded` / `unsupported` /
`requires_operator`) enums are the negotiation vocabulary. RLM may declare
required, preferred, and optional capabilities and expect Lifeloop to fail
closed before dispatch when a `required` capability is unsatisfied.
The `AdapterManifest` registry is shipped: built-in manifests for Codex
and Claude (v1 conformance targets) plus pre-conformance manifests for
Hermes, OpenClaw, Gemini, and OpenCode are exposed through
`manifest_registry()` and the `lifeloop manifest list|show|inspect`
subcommands. RLM prototypes can resolve adapters by `adapter_id` and
`adapter_version` against the registry; the manifest **shape** is
stable in `lifeloop.v0.1`.
### Failure and retry posture
`FailureClass` (13 classes) and `RetryClass` (5 classes) are wire-stable.
`FailureClass::default_retry()` implements the spec's failure-to-retry
mapping table. RLM should avoid text-parsing adapter errors and instead
pattern-match on these enums for retry posture.
## Ownership boundary
**RLM owns recursive inference policy.** Lifeloop owns lifecycle frames,
events, capability negotiation, payload placement, and receipts.
Specifically, Lifeloop will not normalize:
- recursive-inference scheduling, evaluator selection, or budget;
- prompt semantics above payload placement metadata;
- model selection;
- a universal tool/skill/plugin abstraction;
- continuity, memory, recall, or governance (those are CCD-owned, with
CCD itself being just one Lifeloop client).
If RLM needs a recursive-inference primitive that the Lifeloop spec does
not model, the right move is to keep the primitive inside RLM and surface
it to Lifeloop only as opaque payload metadata or as a capability claim.
The spec's *Non-Goals* section is the authoritative boundary.
## How to consume
- Pin `lifeloop = "0.1.x"` (when published) or `lifeloop` from this repo.
- Speak the JSON envelopes via `lifeloop envelope validate <kind>` and
`lifeloop envelope echo <kind>` for fixture tests, or call the typed
Rust API directly. The `DispatchEnvelope` transport-boundary shape
(request + payload envelopes in one stdio document) is the contract
for any subprocess callback client.
- For end-to-end pilots, run `lifeloop event invoke` against your own
callback binary; the command pipes a `DispatchEnvelope` to your
binary's stdin and reads a `CallbackResponse` on stdout.
- Watch `docs/tombstones/` for `v0.x` breaks; each tombstone names the
consumer-side migration steps.
## Cross-references
- Normative spec: [`docs/specs/lifecycle-contract/body.md`](specs/lifecycle-contract/body.md)
- Public types and validation rules: [`src/lib.rs`](../src/lib.rs)
- Wire-shape pinning tests: [`tests/wire_contract.rs`](../tests/wire_contract.rs)
- Fake-client fixture: [`tests/fake_client.rs`](../tests/fake_client.rs)
- Tombstone for the previous schema: [`docs/tombstones/lifeloop.v0.md`](tombstones/lifeloop.v0.md)