# RLM Development Contract Declaration

**Status:** pre-freeze, advisory.
**Schema:** `lifeloop.v0.2`.
**Date:** 2026-05-08.

## Summary

Recursive Language Models (RLM) may begin development against
`lifeloop.v0.2` 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.2` 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.2"` 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. Each payload receipt
includes the source payload's `payload_kind` and optional `content_digest` so
recursive clients can audit which frame payload was placed without Lifeloop
persisting payload bodies.

### 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.2`.

### 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)
