Design pattern for observability-by-default in LLM agents — every feature that performs external I/O, calls an LLM, or makes non-deterministic/heuristic decisions ships a replayable append-only JSONL audit log in the same change. Use when adding or reviewing such a feature (the review question "which log answers why, and can we replay it offline?"), when designing a new audit record schema, when a recurring failure needs corpus-driven repair (replay harness, positive/negative ground truth, regression fixtures from real traffic), or when deciding how to store untrusted text in a log.
Sibling pattern: an instrument reads across the whole store at query time
(distributions, calibration) — see
read-only-instruments. The log is the
corpus; the instrument is one lens over it. A third sibling,
shadow-mode-validation, uses a log of
this kind as the record of a candidate mechanism's would-be decisions.
The pattern sits in the lineage of append-only logs and event sourcing: the
record of what happened is written as an immutable sequence at the moment it
happens, and every later analysis is a read over that sequence.
Principle: a feature with external I/O, LLM calls, or heuristic decisions
ships its audit log in the same change — because the corpus must predate the
failure it will one day explain. Worked example: an agent that submits
generated answers to an external verifier logged every attempt as an ordinary
side effect for weeks; when its parser needed repair, hundreds of real logged
challenges could be replayed offline against a zero-wrong hard gate, and the
repair was validated against real traffic instead of synthetic cases. Ad-hoc
logging added at investigation time can never provide that.
Record schema checklist
Design the record so the run can be replayed offline, not merely read:
Raw input, recoverable — the exact input the decision saw. Untrusted
text (API responses, user/content text) as base64 + sha256, never free
text: a raw log read must not become a prompt-injection path (base64 is
encoding, not sanitization — anything that decodes the payload must treat
it as untrusted again). Bound the stored size with a truncated flag when
cut; a truncated record is thereby marked non-replayable rather than
silently partial, so the replay gate can excuse it instead of trusting it.
Decision path — which branch/tier handled it (e.g. solver_path: code_parse | llm_extract | llm_reason | none).
Reason codes, categorical — every abstain / fallback / failure gets a
machine-groupable code (abstain_reason: reasoning_self_inconsistent),
not prose. A silent fallback is a defect, not a style choice.
Outcome — what happened downstream (accepted / rejected / error),
plus a sanitized error message (stripped to printable characters,
length-capped).
Timestamps + stable keys — ts (ISO, UTC) and a content hash
(sha256) so records dedupe and join across retries.
Writer: append-only JSONL under the agent's data directory, written with
restricted file permissions, best-effort — the feature must not fail because
logging failed.
Ground truth discipline
A log becomes a labeled corpus when outcomes are recorded honestly. This
discipline presupposes that the external system's accept/reject is an
authoritative verdict for that exact input — a flaky verifier, a
policy-driven rejection, or a stale catalog does not pin truth, and outcomes
from such sources belong in the manual-label lane below, not the automatic
ones:
Positive truth — an externally accepted result pins the correct answer
for that exact input (keyed by input hash).
Negative truth — an externally rejected result is durably wrong for
that input, with no manual labeling. Rejections are data; log them with the
same fidelity as successes.
Manual labels — for inputs the outside world never confirmed, keep a
hand-labeled file next to the replay script (manual_labels.json), each
label with provenance (hand-solved / twin-confirmed against an accepted
same-shape input). A null answer marks known-unresolvable cases
(external inconsistency) so the gate can excuse rather than ignore them.
Replay harness pattern
Pure-code script (no LLM, no network) that re-runs the deterministic layer
over every unique logged input.
Hard gate: zero wrong vs known truth (positive + negative). Coverage is
a soft metric — report it, don't gate on it.
Unlabeled parses are printed for labeling; the gate fails until they are
labeled or the behavior abstains.
Exit code 0 only on gate PASS, so the harness can sit in a verification
chain.
Corpus-driven repair loop
When a feature "keeps failing at the same rate":
Aggregate before hypothesizing — success/failure per decision path over
the log; the failing tier is rarely the one you suspect.
Decode and classify every failure into named classes.
Twin-confirm intended fixes — before changing a rule, find a twin:
an already-accepted logged record whose input has the same structural
shape as the failing one, proving how the external system actually treats
that shape. A fix without a twin is a guess about external semantics.
Fix behind the replay hard gate (no lost previously-correct cases), and pin
the failure round as regression fixtures cut from real traffic (base64 in
tests, with provenance comments).
Record the round as a decision-record amendment; leave irreducible cases
explicitly labeled (the floor is part of the finding).
The review-gate question
At the verification step of any change in scope:
When this feature misbehaves, which log will explain why?
Can the behavior be reproduced (replayed) offline from that log?
No answer → back to design, in the same change. Instruments worth imitating
in any agent: a verification audit log (per-attempt decisions of a solver), an
API-drift log (request/response shapes over time), an approval-gate audit
trail (accept/reject with reasons), and LLM telemetry with caller tags (so a
retune can be justified by measured failure-reason rates).
1---2name: replayable-audit-logs3description: Design pattern for observability-by-default in LLM agents — every feature that performs external I/O, calls an LLM, or makes non-deterministic/heuristic decisions ships a replayable append-only JSONL audit log in the same change. Use when adding or reviewing such a feature (the review question "which log answers why, and can we replay it offline?"), when designing a new audit record schema, when a recurring failure needs corpus-driven repair (replay harness, positive/negative ground truth, regression fixtures from real traffic), or when deciding how to store untrusted text in a log.4---56# Replayable Audit Logs (observability by default)78> Sibling pattern: an **instrument** reads across the whole store at query time9> (distributions, calibration) — see10> [`read-only-instruments`](../read-only-instruments/SKILL.md). The log is the11> corpus; the instrument is one lens over it. A third sibling,12> [`shadow-mode-validation`](../shadow-mode-validation/SKILL.md), uses a log of13> this kind as the record of a candidate mechanism's would-be decisions.1415The pattern sits in the lineage of append-only logs and event sourcing: the16record of what happened is written as an immutable sequence at the moment it17happens, and every later analysis is a read over that sequence.1819Principle: **a feature with external I/O, LLM calls, or heuristic decisions20ships its audit log in the same change** — because the corpus must predate the21failure it will one day explain. Worked example: an agent that submits22generated answers to an external verifier logged every attempt as an ordinary23side effect for weeks; when its parser needed repair, hundreds of real logged24challenges could be replayed offline against a zero-wrong hard gate, and the25repair was validated against real traffic instead of synthetic cases. Ad-hoc26logging added at investigation time can never provide that.2728## Record schema checklist2930Design the record so the run can be **replayed offline**, not merely read:3132- [ ] **Raw input, recoverable** — the exact input the decision saw. Untrusted33 text (API responses, user/content text) as **base64 + sha256**, never free34 text: a raw log read must not become a prompt-injection path (base64 is35 encoding, not sanitization — anything that decodes the payload must treat36 it as untrusted again). Bound the stored size with a `truncated` flag when37 cut; a truncated record is thereby marked non-replayable rather than38 silently partial, so the replay gate can excuse it instead of trusting it.39- [ ] **Decision path** — which branch/tier handled it (e.g. `solver_path:40 code_parse | llm_extract | llm_reason | none`).41- [ ] **Reason codes, categorical** — every abstain / fallback / failure gets a42 machine-groupable code (`abstain_reason: reasoning_self_inconsistent`),43 not prose. **A silent fallback is a defect**, not a style choice.44- [ ] **Outcome** — what happened downstream (accepted / rejected / error),45 plus a sanitized error message (stripped to printable characters,46 length-capped).47- [ ] **Timestamps + stable keys** — `ts` (ISO, UTC) and a content hash48 (sha256) so records dedupe and join across retries.4950Writer: append-only JSONL under the agent's data directory, written with51restricted file permissions, best-effort — the feature must not fail because52logging failed.5354## Ground truth discipline5556A log becomes a labeled corpus when outcomes are recorded honestly. This57discipline presupposes that the external system's accept/reject is an58**authoritative verdict** for that exact input — a flaky verifier, a59policy-driven rejection, or a stale catalog does not pin truth, and outcomes60from such sources belong in the manual-label lane below, not the automatic61ones:6263- **Positive truth** — an externally *accepted* result pins the correct answer64 for that exact input (keyed by input hash).65- **Negative truth** — an externally *rejected* result is durably wrong for66 that input, with no manual labeling. Rejections are data; log them with the67 same fidelity as successes.68- **Manual labels** — for inputs the outside world never confirmed, keep a69 hand-labeled file next to the replay script (`manual_labels.json`), each70 label with provenance (hand-solved / twin-confirmed against an accepted71 same-shape input). A **null answer** marks known-unresolvable cases72 (external inconsistency) so the gate can excuse rather than ignore them.7374## Replay harness pattern75761. Pure-code script (no LLM, no network) that re-runs the deterministic layer77 over every unique logged input.782. **Hard gate: zero wrong vs known truth** (positive + negative). Coverage is79 a soft metric — report it, don't gate on it.803. Unlabeled parses are printed for labeling; the gate fails until they are81 labeled or the behavior abstains.824. Exit code 0 only on gate PASS, so the harness can sit in a verification83 chain.8485## Corpus-driven repair loop8687When a feature "keeps failing at the same rate":88891. **Aggregate before hypothesizing** — success/failure per decision path over90 the log; the failing tier is rarely the one you suspect.912. **Decode and classify** every failure into named classes.923. **Twin-confirm intended fixes** — before changing a rule, find a *twin*:93 an already-accepted logged record whose input has the same structural94 shape as the failing one, proving how the external system actually treats95 that shape. A fix without a twin is a guess about external semantics.964. Fix behind the replay hard gate (no lost previously-correct cases), and pin97 the failure round as regression fixtures cut from real traffic (base64 in98 tests, with provenance comments).995. Record the round as a decision-record amendment; leave irreducible cases100 explicitly labeled (the floor is part of the finding).101102## The review-gate question103104At the verification step of any change in scope:105106> When this feature misbehaves, which log will explain why?107> Can the behavior be reproduced (replayed) offline from that log?108109No answer → back to design, in the same change. Instruments worth imitating110in any agent: a verification audit log (per-attempt decisions of a solver), an111API-drift log (request/response shapes over time), an approval-gate audit112trail (accept/reject with reasons), and LLM telemetry with caller tags (so a113retune can be justified by measured failure-reason rates).
Run npx skillmds@latest add shimo4228/replayable-audit-logs in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Design pattern for observability-by-default in LLM agents — every feature that performs external I/O, calls an LLM, or makes non-deterministic/heuristic decisions ships a replayable append-only JSONL audit log in the same change. Use when adding or reviewing such a feature (the review question "which log answers why, and can we replay it offline?"), when designing a new audit record schema, when a recurring failure needs corpus-driven repair (replay harness, positive/negative ground truth, regression fixtures from real traffic), or when deciding how to store untrusted text in a log. It is listed under AI & ML on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
shimo4228 (@shimo4228) published this skill. Their other Agent Skills are listed on their SkillMD profile.