# Live adapter conformance evidence
This directory holds **sanitized** capture artifacts from real Claude
Code and Codex CLI sessions exercising Lifeloop's lifecycle integration
assets. The fixtures here back the v1 freeze gate 1 ("Adapter
evidence") in `docs/release-gates.md`.
> **The captured artifacts are not test inputs in the conventional
> sense.** They are the *reference observations* that the manifest
> claims must remain consistent with. Tests in
> `tests/manifest_claims.rs` and (forthcoming) `tests/live_evidence*`
> compare these fixtures against `lifeloop::claude_manifest()` and
> `lifeloop::codex_manifest()`. A mismatch is not a flaky test — it
> is a manifest claim that has lost its grounding in real harness
> behavior.
## Layout
```
tests/fixtures/live/
├── README.md ← this file
└── claude/ ← Claude Code captures (#30)
├── README.md ← per-session capture index, written by recapture
└── <event-name>/<n>.json ← one sanitized event per file
```
Per-event subdirectories use the **harness-defined wire event name**
(`SessionStart`, `UserPromptSubmit`, `PreCompact`, `Stop`,
`SessionEnd` for Claude). Each captured event is one JSON file,
suffixed by a stable ordinal so multiple captures within one
session order deterministically.
> **Note on Codex:** Live Codex CLI evidence (#31) landed via
> `tests/fixtures/live_codex/codex_cli_<version>/` (see MR !36),
> with per-CLI-version captures rather than per-event-subdir.
> The two fixture trees serve the same v1-evidence purpose but
> use different shapes; this rig and the per-Codex-version layout
> coexist as parallel evidence sources. The sanitizer's
> `CODEX_RULES` are kept (deny-by-default stub) in case a future
> Codex consumer wants to reuse this layout.
The `<harness>/README.md` file is itself a committed artifact: it
records the harness version, the CCD version, the date of the
capture, and the operator who took it. Recapture overwrites it.
## Sanitization rules
Every committed fixture must pass through the sanitizer before
landing. The sanitizer is `scripts/sanitize-live-evidence.py`. The
contract is:
1. **Absolute filesystem paths → placeholders.** The operator's
`cwd`, transcript paths, log paths, etc. become `<REPO_ROOT>`,
`<TRANSCRIPT_PATH>`, `<LOG_PATH>` literals. The sanitizer has a
dedicated rule per known field; an unknown path-shaped field
triggers a sanitizer error rather than silently leaking a path.
2. **Session / run / task identifiers → deterministic placeholders.**
`session_id`, `run_id`, `task_id`, and any UUID/ULID-shaped value
becomes `<SESSION_ID>` / `<RUN_ID>` / `<TASK_ID>` etc. — same
placeholder every time so fixture diffs stay readable.
3. **User prompt content → shape-only placeholders.** The body of
`prompt` (UserPromptSubmit) is replaced by `<USER_PROMPT_BODY>`.
We capture the lifecycle *shape* (event fired, fields present),
not the operator's prompt text.
4. **Timestamps → deterministic placeholder.** Wall-clock fields
become `<TS>` so re-captured fixtures don't churn on every run.
5. **Hostnames, usernames, machine identifiers → placeholders.**
`<HOSTNAME>`, `<USERNAME>`, etc. The sanitizer enumerates the
known fields; unknown fields surface as errors so we don't leak
environment.
6. **Process IDs and other ephemeral integers → placeholders.** PIDs
become `<PID>`; other ephemeral integers carry an explanatory
suffix (`<PID>`, `<EPOCH_MS>`, ...).
The sanitizer is **deny-by-default for unknown shapes**: if a field
appears in raw capture that the sanitizer doesn't have an explicit
rule for, sanitization fails loudly. This keeps the rig from
silently committing leaked data when a Claude version adds a new
field.
## Recapture procedure
See `docs/playbooks/capture-live-adapter-evidence.md`. The high-level
flow is:
1. Operator runs `scripts/regenerate-live-claude-evidence.sh` (or
the codex counterpart) — the script sets up a scratch repo,
installs Lifeloop's host-asset rendering with a capture-wrapper
`CCD_BIN`, and prints the operator commands to drive Claude /
Codex through the lifecycle paths to capture.
2. Operator drives the harness session as instructed.
3. Operator runs the sanitizer over the raw captures. Sanitized
output lands in `tests/fixtures/live/<harness>/`, ready to
commit.
4. Operator runs `make verify` — the live-evidence comparison tests
re-run against the freshly committed fixtures.
## Why a rig MR before any captured fixtures
This MR (#30 capture-rig precursor) lands the directory layout,
sanitization rules, and the recapture scripts on its own — without
any captured fixtures yet. Reasons:
- **Reviewability.** The rig MR is small and focused on the safety
surface (sanitizer rules, fixture conventions). It can be reviewed
for "does this redact what it claims to redact?" without being
bundled with any actual harness output.
- **Reusability across harnesses.** The rig is harness-generic; a
future Codex consumer (or any non-Claude callback client whose
evidence shape resembles Claude's hook payload) can reuse the
sanitizer and orchestrator without re-discovering the
deny-by-default and atomic-write conventions. Codex live evidence
for #31 ultimately landed via a different shape (per-CLI-version
fixtures under `tests/fixtures/live_codex/`, MR !36); the two
fixture trees coexist.
- **Diagnosability.** When captured fixtures land later (#30 evidence
MR), reviewers can audit the rig as a frozen prior MR rather than
untangling rig-vs-fixture concerns in the same diff.
If the rig itself needs changes after fixtures have landed, that
becomes a follow-up MR with a deliberate sanitizer-version bump and a
fresh recapture.