agent-flight-recorder
Agent Flight Recorder
Purpose
Keep a black-box flight recorder. Whenever you deviate from the straight path during a task (retry, workaround, unexpected setup, missing context, quality rework, blocker), create a concise log entry.
You are the recorder, not the analyst.
Activation
Always active. You do not need to be asked.
-
Record silently. Do not mention logging mid-task.
-
Logging must not slow you down or change your approach.
-
At task end (or abort), if entries exist, add exactly one line to your final response:
Flight recorder: N entries logged. See <path>. -
If zero entries, say nothing.
Project-level visibility (optional)
If a project wants explicit policy text, add a short note to that project's
agent instruction file (for example AGENTS.md or CLAUDE.md) stating that
agent-flight-recorder is always-on for multi-step work and that only
deviations are logged.
Run log file (one run per file)
Create a new file per run:
<project-root>/.agents/flight-recorder/flight-YYYY-MM-DD-HHMMSS-TZ.md
- Use local time if known, otherwise UTC and write
UTCas TZ. - If a name collision occurs, append
-1,-2, ... until unique. - Do not edit old run files.
- Within the current run file, entries are append-only.
- If the directory does not exist, create it.
Repository hygiene default:
- In git repositories, ignore recorder output by default with
/.agents/flight-recorder/in.gitignore. - Opt out only if the project intentionally versions recorder logs, and document that decision in project instructions.
When creating a new run file, write this header once:
schema: flight-recorder/v2.5
run_started: 2026-02-11T09:10:00Z
task: "Implement auth middleware" # optional, one-liner describing the task
recorder_agent: claude-code # agent product or CLI name
recorder_model: claude-opus-4-6 # exact model ID as reported by runtime
recorder_effort: high # optional, omit if runtime does not expose
Header rules:
recorder_agent: the agent product or CLI that is running (e.g.claude-code,codex-cli,chatgpt,cursor,aider). Use the canonical short name, not a marketing name. Useunknownonly if not determinable.recorder_model: the exact model identifier as reported by the runtime (e.g.claude-opus-4-6,gpt-4o-2025-06-01,o3). Prefer the full model ID over a friendly name. Useunknownonly if not exposed.recorder_effortis optional. If the runtime exposes a reasoning effort or compute budget setting, report its value verbatim. Omit the field entirely if the runtime has no such concept.taskis optional. If set, use one short sentence describing the user-facing goal of the run. Omit if the task is unclear at run start.
Fallback
If file write is impossible, include the full log inline at the end of your final response under:
## Flight recorder log (inline)
Also state in one sentence that file write failed.
Run footer
When the run ends (task completion or abort), append a footer block after the last entry:
run_ended: 2026-02-11T09:45:00Z
entries: 4
high_severity: 1
outcome: completed | aborted | interrupted
Footer rules:
- Write the footer in the same final flush that writes any remaining entries.
outcomeiscompletedwhen the task finishes normally,abortedwhen the agent gives up, andinterruptedwhen the session ends unexpectedly (best effort).- If there are zero entries, do not create a run file and therefore no footer.
Write strategy (batch by default)
Default: keep entries in memory and write/append to file only:
- at task end or abort,
- immediately before asking the user a blocking clarification question,
- immediately before a major context switch where you might lose state,
- immediately after creating a
highseverity entry.
When you flush, append all accumulated entries in one write.
When to write an entry (only deviations)
Write an entry when any of the following occurs and it is a deviation from the expected path:
| trigger | default severity | meaning |
|---|---|---|
| detour | medium | You changed approach because the expected path failed. |
| setup | medium | Unexpected install/config required. |
| retry | medium | You repeated a step due to failure or ambiguity. |
| missing-context | low | You had to ask for info that should have been in initial context, or needed a blocking clarification. |
| quality | high | Output required rework to meet expected standard. |
| slow-step | high | A single step was unusually slow (ordinal estimate). |
| assumption | low | You proceeded on an assumption that could be wrong. |
| blocker | high | You could not proceed without external action. |
Notes:
- Do not log normal expected steps.
missing-contextis not normal interaction. Log it only when it blocks progress or reveals missing initial inputs.- Override severity if warranted.
- One real-world incident maps to one entry. Do not duplicate the same incident in another entry.
Entry format
Each entry is a fenced YAML block for reliable parsing. Keep it concise.
IDs are assigned per run as e1, e2, e3, in the order you flush them.
why must be one short sentence (max 15 words).
Time field:
- Every entry must include
atin ISO-8601 with timezone, for example2026-02-12T14:17:09+02:00or2026-02-12T12:17:09Z. atshould represent when the deviation happened (best known time), not when the buffered entries were flushed to disk.- If exact second is unknown, estimate reasonably and keep ordering consistent.
Formatting invariants:
- Every entry must be exactly one fenced block: start with ````yaml` and end with ``` on its own line.
- Never emit free-form
yamllabel lines or partial/unfenced YAML content. - A formatting correction is not a new incident. Do not use
repeat_offor markup repair of the same occurrence. - Quote free-text values when they contain YAML-significant characters
(especially
:,#, or backticks), using double quotes. - This applies especially to
signal,stuck, andworkaroundbecause they often contain shell error text.
Standard YAML entry
id: e1
at: 2026-02-12T12:17:09Z
severity: high | medium | low
trigger: detour | setup | retry | missing-context | quality | slow-step | assumption | blocker
situation: One sentence. What you were trying to do.
stuck: One sentence. Where exactly it got stuck.
why: One short sentence. Best hypothesis.
workaround: What you did to get past it (or attempted).
resolved: true | false | partial
# Cost signal (do NOT invent minutes unless measured)
waste: low | medium | high
waste_min: 10 # optional, only if measured or explicitly provided
time_basis: timestamped | command_duration | explicit_user | other # required if waste_min present
retries: 2 # optional integer
# Optional routing/context
scope: env | build | deploy | web | parsing | auth | data | test | config | deps | perf | other
file: path/to/relevant/file
repeat_of: e3
signal: A short symptom or error signature to recognize early
# If value contains `: `, `#`, or backticks, quote it:
# signal: "zsh: command not found: yaml"
Compact YAML entry (low severity only)
Use this for low-severity entries to keep logging cheap. Do not use compact format for medium or high severity entries; always use the standard format for those.
id: e4
at: 2026-02-12T12:23:44Z
severity: low
trigger: assumption | missing-context
situation: One sentence.
stuck: One sentence.
why: One short sentence. Best hypothesis.
resolved: true | false | partial
waste: low | medium | high
signal: Optional short symptom.
Rules
- Recorder-only: do not include analysis, developer guidance, or improvement suggestions.
- Do not include secrets, tokens, raw user data, or PII. Summarize and anonymize.
- Prefer writing the entry after the deviation is resolved, so
resolvedandretriesare accurate. - Do not edit earlier entries. If something repeats, create a new entry with
repeat_of. - Use
repeat_ofonly when the same signature happens again later in the run, not for a duplicate of the same occurrence.