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