# Tombstone: lifeloop.v0

**Superseded by:** lifeloop.v0.1
**Date:** 2026-05-08
**Reason:** extend contract to match docs/specs/lifecycle-contract/body.md: add frame.* events, receipt-status rewrite, full failure/retry-class enums, callback envelopes, payload envelope, frame context

## Removal criteria

This tombstone may be deleted once **all** of the following hold:

- `lifeloop.v1` has been frozen (per the schema lifecycle gate in
  `docs/specs/lifecycle-contract/body.md`: CCD plus one non-CCD client class
  consuming the same contract, and the required conformance adapter set),
- the only known external consumer of `lifeloop.v0` (the CCD draft client
  in this repo's git history at commit `194091b`) has migrated to a
  `lifeloop.v0.x` or later schema, **and**
- two consecutive Lifeloop conformance fixture runs have observed zero
  `lifeloop.v0` literals in any committed fixture.

The pre-freeze `lifeloop.v0.x` line is intentionally not part of these
criteria — every `v0.x` step writes its own tombstone for its own
predecessor.

## Migration

Consumers moving from `lifeloop.v0` to `lifeloop.v0.1` must:

1. **Schema string.** Update any pinned `schema_version` literal from
   `"lifeloop.v0"` to `"lifeloop.v0.1"`. Wire tests pin literal strings;
   the rename is mechanical.
2. **Lifecycle event vocabulary.** Replace the prompt/turn-shaped events
   with the frame-shaped events from the spec:
   - `prompt.building` → `frame.opening` (and add `frame.opened` for the
     post-delivery moment),
   - `turn.ended` → `frame.ended` (and add `frame.ending` for the
     pre-close moment),
   - new events: `receipt.gap_detected`.
3. **Adapter manifest fields.** The previous `harness` field is replaced
   by `adapter_id` plus `adapter_version`. The previous
   `CapabilityManifest` shape is replaced by the spec's adapter manifest
   shape (lifecycle event support map, integration-mode array, placement
   support, telemetry, session identity, receipts, statefulness, known
   failure classes).
4. **Capability/support states.** `CapabilityLevel { Native, Simulated,
   Manual, Unavailable }` is replaced by the five-state `SupportState`:
   `native`, `synthesized`, `manual`, `partial`, `unavailable`. Notes:
   `simulated` was renamed to `synthesized` (clearer about the direction
   of derivation), and `inferred` was folded into `partial`
   (telemetry-derived behavior is partial behavior). Pre-issue-#6
   drafts named both `simulated` and `inferred`; the landed shape is
   five variants. Wire names are lowercase snake_case via
   `serde(rename_all = "snake_case")`. See
   `docs/specs/lifecycle-contract/body.md` "Support states".
5. **Receipt statuses.** The previous `{ accepted, completed, degraded,
   rejected, failed }` set is replaced by the spec's `{ observed,
   delivered, skipped, degraded, failed }`. Map `accepted` → `observed`,
   `completed` → `delivered`, `rejected` → `failed` with a
   `failure_class`.
6. **Failure classes.** The previous six-class enum (`capability_*`,
   `invalid_payload`, `adapter_failure`, `harness_unavailable`,
   `timeout`) is replaced by the spec's thirteen classes. Notable splits:
   `adapter_failure` becomes `adapter_unavailable` plus
   `transport_error`; `invalid_payload` becomes `payload_too_large` plus
   `payload_rejected` plus `invalid_request`; new classes include
   `placement_unavailable`, `identity_unavailable`, `operator_required`,
   `state_conflict`, `internal_error`.
7. **Retry classes.** The previous `{ do_not_retry, retry_same_adapter,
   retry_after_backoff, retry_different_adapter, ask_operator }` set is
   replaced by the spec's `{ safe_retry, retry_after_reread,
   retry_after_reconfigure, retry_after_operator, do_not_retry }`. The
   default mapping table in the spec gives a deterministic translation.
8. **Receipts gain required-but-nullable fields.** `client_id`,
   `adapter_id`, `invocation_id`, `sequence`, `parent_receipt_id`,
   `idempotency_key`, `integration_mode`, `at_epoch_s`, `payload_receipts`,
   and `warnings` are now part of the receipt shape per the spec. Existing
   `lifeloop.v0` receipts cannot be replayed on `v0.1` without
   reconstructing these fields; treat them as one-shot diagnostics.
9. **New types.** `CallbackRequest`, `CallbackResponse`, `PayloadEnvelope`,
   `FrameContext`, and the placement / requirement / negotiation /
   adapter-role enums are all new in `v0.1`. There is no `v0` predecessor
   to migrate.

`v0.x` is the pre-freeze line; further breaking changes to receipt or
callback shape may land before `v1`. Producers should follow each
tombstone's migration notes; consumers should pin the exact `v0.x`
version they support.

## `CCD_*` telemetry env vars (issue #5)

**Superseded by:** `LIFELOOP_*` env-var aliases for telemetry config.
**Date:** 2026-05-08
**Reason:** lifecycle telemetry parsing moves into Lifeloop. The env
vars that name harness telemetry sources and overrides need a
Lifeloop-owned spelling so non-CCD clients (RLM and others) are not
forced to set `CCD_*` keys. The CCD spellings remain honored as
compatibility inputs through `lifeloop.v0.x`; when both are set, the
`LIFELOOP_*` value wins and a single bounded warning per resolved key
per process is emitted via `lifeloop::telemetry::env_warning_sink`.

### Aliases (lifeloop ↔ ccd_compat)

General fallbacks:

- `LIFELOOP_CONTEXT_WINDOW_TOKENS` ↔ `CCD_CONTEXT_WINDOW_TOKENS`
- `LIFELOOP_HOST_MODEL` ↔ `CCD_HOST_MODEL`
- `LIFELOOP_HOST_ADAPTER` ↔ `CCD_HOST_ADAPTER`

Claude:

- `LIFELOOP_CLAUDE_HOME` ↔ `CCD_CLAUDE_HOME`
- `LIFELOOP_CLAUDE_SESSION_ID` ↔ `CCD_CLAUDE_SESSION_ID`
- `LIFELOOP_CLAUDE_MODEL` ↔ `CCD_CLAUDE_MODEL`
- `LIFELOOP_CLAUDE_CONTEXT_WINDOW_TOKENS` ↔ `CCD_CLAUDE_CONTEXT_WINDOW_TOKENS`

Codex:

- `LIFELOOP_CODEX_HOME` ↔ `CODEX_HOME` (native Codex env name; not
  CCD-prefixed but treated as a legacy compatibility input)
- `LIFELOOP_CODEX_THREAD_ID` ↔ `CODEX_THREAD_ID`
- `LIFELOOP_CODEX_MODEL` ↔ `CCD_CODEX_MODEL`

Gemini:

- `LIFELOOP_GEMINI_TELEMETRY_PATH` ↔ `CCD_GEMINI_TELEMETRY_PATH`
- `LIFELOOP_GEMINI_TELEMETRY_OUTFILE` ↔ `GEMINI_TELEMETRY_OUTFILE`
- `LIFELOOP_GEMINI_MODEL` ↔ `CCD_GEMINI_MODEL`
- `LIFELOOP_GEMINI_CONTEXT_WINDOW_TOKENS` ↔ `CCD_GEMINI_CONTEXT_WINDOW_TOKENS`

OpenCode:

- `LIFELOOP_OPENCODE_DB_PATH` ↔ `CCD_OPENCODE_DB_PATH`
- `LIFELOOP_OPENCODE_DATA_DIR` ↔ `CCD_OPENCODE_DATA_DIR`
- `LIFELOOP_OPENCODE_DATA_DIR_NATIVE` ↔ `OPENCODE_DATA_DIR`
- `LIFELOOP_OPENCODE_MODEL` ↔ `CCD_OPENCODE_MODEL`
- `LIFELOOP_OPENCODE_CONTEXT_WINDOW_TOKENS` ↔ `CCD_OPENCODE_CONTEXT_WINDOW_TOKENS`

### Removal criteria

The `CCD_*` (and bare native names like `CODEX_HOME`,
`CODEX_THREAD_ID`, `GEMINI_TELEMETRY_OUTFILE`, `OPENCODE_DATA_DIR`)
compatibility inputs may be dropped from `lifeloop::telemetry` once
**all** of the following hold:

- `lifeloop.v1` has been frozen (per the schema lifecycle gate in
  `docs/specs/lifecycle-contract/body.md`),
- the only known external consumer of the legacy spellings (the CCD
  draft client at the time `lifeloop.v0` was tombstoned) has cut over
  to the `LIFELOOP_*` aliases on every machine that runs it, **and**
- two consecutive Lifeloop conformance fixture runs have observed zero
  bounded-warning emissions for any of the listed aliases.

Until then, callers that set `CCD_*` keep working unchanged.
