# Lifeloop
Lifeloop is a provider-neutral lifecycle abstraction and normalizer for AI
harnesses.
It exists because AI harness lifecycle is a reusable substrate. Claude Code,
Codex, Gemini, Hermes, OpenClaw, OpenCode, and future harnesses expose session,
prompt, context-pressure, turn, closeout, and recovery moments through different
hooks and telemetry conventions. Lifeloop normalizes those lifecycle moments so
clients can attach through a stable contract instead of importing CCD or
rebuilding harness adapters.
## Role
```text
harnesses
Claude / Codex / Gemini / Hermes / OpenClaw / OpenCode
|
v
Lifeloop
lifecycle events, receipts, capabilities, payload delivery
|
+------------+-------------+-------------+
| | | |
CCD RLM Reforge Fixity
continuity recursion memory family constraints
```
## Boundary
Lifeloop owns lifecycle normalization:
- harness identity and adapter manifests;
- lifecycle event vocabulary;
- hook timing normalization;
- capability negotiation;
- lifecycle receipts;
- payload placement and delivery metadata;
- lifecycle-relevant telemetry;
- failure classes and retry posture.
Lifeloop does not own continuity, memory, recursive inference policy, model
selection, prompt semantics above placement metadata, tool abstraction, skills,
or a universal IDE.
## Why Rust
Lifeloop starts as an extraction from CCD, and CCD is Rust. Keeping Lifeloop in
Rust minimizes translation risk during extraction, preserves behavior around
the existing lifecycle normalizer, and gives the public event/capability/receipt
contracts strong local types. The callback protocol remains language-neutral,
so Go, Python, shell, or research clients can consume Lifeloop through JSON
envelopes without importing Rust.
## Current Shape
The crate implements the `lifeloop.v0.2` slice of the contract:
- lifecycle event vocabulary (14 events, including the `frame.*` family,
`context.compacted`, and `receipt.gap_detected`);
- callback request and response envelopes (`CallbackRequest` /
`CallbackResponse`) plus a `DispatchEnvelope` transport-boundary shape
carrying request + payload envelopes through one stdio document
(issue #22);
- adapter manifest registry with built-in manifests for Codex and
Claude as v1 conformance targets and pre-conformance manifests for
Hermes, OpenClaw, Gemini, and OpenCode (issue #6); shared
conformance fixture suite under `tests/conformance/` (issue #10);
- payload envelope with `placement` / `requirement` / `outcome`
enums, opaque-by-construction renderer (bodies are transported
verbatim so overlapping JSON keys across payloads stay
distinguishable, per issue #21);
- lifecycle receipt with required-but-nullable correlation fields and
per-payload `payload_receipts` carrying payload kind and optional digest
provenance;
- 13-class `FailureClass`, 5-class `RetryClass`, and the spec's
failure-to-retry default mapping;
- 5-state `SupportState` (`native`, `synthesized`, `manual`,
`partial`, `unavailable`) for adapter capability claims;
- optional renewal/reset capability claims that distinguish native,
wrapper-mediated, manual, and unavailable reset paths from
observation-only versus payload-delivering continuation support;
- lifecycle router stack: pre-dispatch validation + adapter resolution
→ capability/placement negotiation → callback invocation
(in-process or subprocess over JSON stdio via `DispatchEnvelope`) →
receipt synthesis with idempotency (issues #7 / #13 / #14 / #15);
- subprocess callback transport with deadline-bounded round trip
(issues #8 / #22);
- host integration assets (`.claude/settings.json`,
`.codex/config.toml`, etc.) rendered through `src/host_assets.rs`
with a CCD-flavored compat profile (issues #4 / #9; profile
abstraction is in flight as issue #26);
- thread-sync publisher (`crates/thread-sync-publisher/`) as the
first non-CCD lifecycle client, consuming Lifeloop receipts over
the same callback contract (issue #11);
- Fixity pilot (`crates/fixity-pilot/`) as the #28 non-CCD product
pilot, consuming `DispatchEnvelope` payloads through real
`lifeloop event invoke` dispatch and keeping Reforge-inspired
repeated-signal policy outside Lifeloop core.
The `lifeloop` CLI is intentionally thin — every subcommand is a
shim over the library:
```bash
lifeloop events # print the event vocabulary
lifeloop envelope validate request # validate JSON read from stdin
lifeloop envelope validate response
lifeloop envelope echo request
lifeloop event invoke # validate -> negotiate -> callback -> receipt
# accepts a DispatchEnvelope on stdin
lifeloop manifest list # registry-backed adapter listing
lifeloop manifest show <id>
lifeloop manifest inspect <id>@<ver>
lifeloop asset preview --host <h> --mode <m> # render host assets
lifeloop telemetry snapshot --host <id>
lifeloop receipt emit # synthesize a receipt from a wire envelope
lifeloop receipt show
lifeloop conformance run # walk tests/conformance/ fixtures
lifeloop version
```
Run `lifeloop --help` for argument detail, exit-code semantics, and
the spec pointer.
## Docs
- [Product thesis](docs/product-thesis.md)
- [Release gates and v0.2/v1 roadmap](docs/release-gates.md)
- [Lifecycle contract spec (normative)](docs/specs/lifecycle-contract/body.md)
- [Harness concept boundary note](docs/harness-concept-boundary.md)
- [Renewal reset capability boundary](docs/decisions/renewal-reset-capability.md)
- [Source-file ownership decision](docs/decisions/source-files-are-user-owned.md)
- [RLM development-contract declaration](docs/rlm-development-contract.md)
- [Fixity pilot client](docs/client-pilots/fixity.md)
- [Wire-contract review playbook](docs/playbooks/wire-contract-review.md)
- [Schema-bump playbook](docs/playbooks/schema-bump.md)
## Verification
```bash
make verify
```
Focused mutation sweeps are available through `make mutants-all`, which writes
slice evidence under `mutants.out/all`. The mutation gate excludes only the
documented behavior-equivalent survivors in `Makefile`: subprocess deadline
polling cadence and host-asset no-op rendering branches. Treat any new missed
mutant as a test gap unless it has an explicit equivalence rationale.