Write structured run logs for agent work that the next run can actually use: timestamp conventions, a greppable JSONL event schema, decision trails, failure notes that help instead of confuse, and log hygiene when several agents write to one project. Triggers when an agent needs to keep a working log, hand work off, debug from logs, or coordinate logging across parallel runs.
./install.sh log_structured_thinkingname: log_structured_thinking description: Write structured run logs for agent work that the next run can actually use: timestamp conventions, a greppable JSONL event schema, decision trails, failure notes that help instead of confuse, and log hygiene when several agents write to one project. Triggers when an agent needs to keep a working log, hand work off, debug from logs, or coordinate logging across parallel runs.
Most agent logs are written for a reader who already knows the system. The next agent reading your log has no prior context, cannot ask you a question, and will act on what it finds. This skill is the convention for writing run logs that survive that handoff: one event per line, stable field names, decisions recorded with their reasons, and failures recorded with enough state to reproduce. It also covers the mechanics of keeping logs sane when several agents write to the same project at once.
Two layouts cover almost everything:
project/logs/run.log) when one agent runs at a time. Simple, and tail shows the recent story.project/logs/runs/<ts>_<short-task>/log.jsonl) when runs overlap or get re-run. Name the timestamp YYYYMMDD-HHMMSS in the project timezone. Keep a latest symlink pointing at the newest directory.Pick per-run directories the moment more than one agent might be writing. One file with two writers interleaving mid-line is worse than no log at all.
Timestamps: ISO 8601 with the timezone offset, like 2026-09-27T11:37:00-05:00. Always with the offset, never bare. All events in one log use one timezone.
Generate a run id at the start of the run and attach it to every event: <yyyymmdd>-<short-task>-<seq>, e.g. 20260927-skill-forge-b-01. This is the highest-leverage single habit. A run id turns "show me everything about that run" from a research project into one filter.
One event per line, nothing else on the line. No prose sentences carrying the data. Field names are stable, snake_case, and never renamed later.
Required fields on every event:
| Field | Contents | |---|---| | ts | ISO 8601 timestamp with offset | | run_id | The run id from step 2 | | agent | Who wrote it (agent name or session id) | | event | Stable snake_case name: run_start, attempt, decision, failure, handoff, run_end | | level | debug, info, warn, or error (meanings fixed below) | | msg | One short human-readable line |
Optional fields, used whenever they add evidence:
| Field | Contents | |---|---| | tried | What was attempted (command, tool call, approach) | | result | What actually happened (verbatim output, exit code, error text) | | reason | Why this decision was made | | state | Key state needed to reproduce: ids, versions, file paths, before/after values | | next | The intended next step | | links | Related ids: claim slugs, ticket numbers, commit hashes |
Two rules that matter more than the format:
claim_rejected, never "the claim was rejected". Stable names let you count events across runs and spot what changed.| Level | Meaning | |---|---| | error | A step failed and will not succeed without intervention. Start debugging here. | | warn | Something degraded but recovered: a retry succeeded, a fallback was used, a lock was skipped. Context, not cause. | | info | A real event happened: run started, decision taken, artifact written, handoff done. Timeline material. | | debug | Internal step detail. Only read when tracing one run. |
If it would not wake someone up, it is not error. Agents take levels literally and will chase the first error they see, so keep them honest.
When you choose an approach, record the call so the next run does not re-litigate it:
json
{"ts":"2026-09-27T11:40:12-05:00","run_id":"20260927-skill-forge-b-01","agent":"skill-forge-b","event":"decision","level":"info","msg":"chose per-run directories over one shared file","reason":"two sibling forges may run in the same hour; shared append risks interleaved lines","state":{"alt_rejected":"single shared run.log","lock_available":"no lock mechanism in project"},"next":"proceed with runs/20260927-113700_skill-forge-b/log.jsonl"}
Include: what was chosen, the runner-up, and the reason. A decision without its reason is just a fact waiting to be re-debated. Killed approaches belong here too, with the evidence that killed them.
A stack trace says where it broke. It rarely says why. Ask: "what would I need to reproduce this without asking anyone?" Then log that.
json
{"ts":"2026-09-27T11:44:03-05:00","run_id":"20260927-skill-forge-b-01","agent":"skill-forge-b","event":"failure","level":"error","msg":"publish API returned 422 on second attempt","tried":"POST /api/v1/skills with skill.json","result":"422: description exceeds 500 chars (had 512)","state":{"skill_json_bytes":18230,"description_chars":512,"api_version":"v1"},"next":"trim description to under 500 chars and retry"}
Minimum for a failure event: what you tried, the verbatim result (error text, exit code), and the relevant state (ids, versions, input sizes). Hypothesis goes in next or msg, clearly labeled as a guess. Never log secrets: mask tokens and keys to first/last 4 chars or hash them.
Pointing a reader at a multi-thousand-line log burns context on noise. Ship a small query helper with the log convention and tell readers to use it. A 20-line script beats a paragraph of instructions.
bash
#!/usr/bin/env bash
# logq: query agent run logs. usage:
# logq run <run_id> full timeline for one run
# logq errors [--since 24h] aggregate errors
case "$1" in
run)
grep -h "\"run_id\":\"$2\"" logs/runs/*/log.jsonl 2>/dev/null | jq -c 'select(.run_id=="'"$2"'")' | jq -s 'sort_by(.ts)' ;;
errors)
grep -h '"level":"error"' logs/runs/*/log.jsonl 2>/dev/null | jq -r '.event' | sort | uniq -c | sort -rn ;;
esac
When debugging, pull one failing run and one passing run and diff the two before theorizing. That single comparison stops readers from pattern-matching on the first scary line.
event: "claim", event: "release") with agent and ts..agent-locks/<path>.lock holding agent-id, task, started, and expires timestamps prevents two agents editing the same file. Expired locks can be re-claimed; unexpired ones are respected.event: "correction", links pointing at the wrong line), not edits.{"event":"run_start", ...} to the shared log even when the detail lives in the per-run directory. The index lets any reader find every run without globbing.When you adopt this skill on a project, you produce:
logq-style script) committed with the logs, documented in the project's instruction file.run_end event carrying the outcome ("ok", "failed", "partial") and a one-line summary in msg.A worked example of a full run log plus the queries against it lives in references/run-log-example.md.
publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e
No ratings yet — be the first to rate it.