7a08259db7
Sessions that ran Workflow-substrate pipelines read as near-zero subagent spend: analyze_subagents() globs one level only, while Workflow runs log under workflows/wf_*.json (per-agent summary) and subagents/workflows/<runId>/agent-*.jsonl (raw usage). New analyze_workflows() joins both — raw transcripts authoritative (dedup by requestId, as everywhere), summary supplies stage labels and models, label-prefix grouping (impl:3 -> impl), untracked transcripts isolated under '?'. Report gains workflows[] and workflow_totals; totals now include workflow spend. Live check against a real 17-agent workflow run: raw 1.30M vs reported 1.22M (the live counter's known ~7-11% under-tracking). closes #28
229 lines
10 KiB
Markdown
229 lines
10 KiB
Markdown
---
|
||
name: postmortem
|
||
description: Use after a working session (or a `/boss` run) to grade how well the toolchain and the dispatched agents actually performed, and how many tokens the run spent. Reads the session's own Claude Code transcript + subagent logs + Workflow run logs, aggregates tokens/tool-health deterministically via a helper script, and writes a scored retrospective. Utility skill, user-invoked or orchestrator-invoked at run close; never a pipeline gate.
|
||
---
|
||
|
||
# postmortem — grade the run
|
||
|
||
> **Violating the letter of these rules is violating the spirit.**
|
||
|
||
## Overview
|
||
|
||
Every session leaves a detailed flight recorder behind — the
|
||
JSONL transcript under `~/.claude/projects/<slug>/<session>.jsonl`,
|
||
one sidechain log per dispatched subagent under
|
||
`<session>/subagents/`, and for every Workflow run a per-agent
|
||
summary in `<session>/workflows/wf_*.json` with raw per-agent
|
||
transcripts under `<session>/subagents/workflows/<runId>/`. Nobody
|
||
reads it. So the same expensive
|
||
habits repeat: a subagent fleet that bounces `BLOCKED`, a tool
|
||
loop with a 30% error rate, a cache-cold run that re-sends the full
|
||
input on every turn. This skill turns that recorder into a graded
|
||
retrospective: **how well did the toolchain work, how effective
|
||
were the agents, and how many tokens did it spend.**
|
||
|
||
It is descriptive, not corrective. It produces a report and, where
|
||
warranted, recommends filing tracker issues — it does not change
|
||
code, baselines, or the pipeline.
|
||
|
||
## When to Use / Skipping
|
||
|
||
Invoke:
|
||
|
||
- After a substantial interactive session, when you want to know
|
||
where the time and tokens went.
|
||
- At the close of a `/boss` autonomous run, to grade the
|
||
orchestrator's dispatch choices.
|
||
- When a run *felt* token-heavy or thrashy and you want the numbers.
|
||
|
||
Not a pipeline phase. Never a hard-gate. It reads logs; it never
|
||
blocks a cycle.
|
||
|
||
## The Iron Law
|
||
|
||
```
|
||
NUMBERS COME FROM THE SCRIPT, NEVER FROM MEMORY OR EYEBALLING THE LOG
|
||
REPORT TOKENS AS RAW COUNTS — NEVER CONVERT THEM TO A COST OR PRICE
|
||
NEVER READ ~/.ionos_token, .credentials.json, OR ANY SECRET FILE INTO CONTEXT
|
||
A GRADE WITHOUT A CITED METRIC IS AN OPINION — DROP IT
|
||
DEDUP TOKEN USAGE BY requestId — THE SCRIPT DOES THIS; DO NOT RE-SUM THE RAW LOG BY HAND
|
||
```
|
||
|
||
## What the data does and does not contain
|
||
|
||
Read before interpreting, so the report never overclaims:
|
||
|
||
- **Present:** per-request `usage` (input / output / cache-creation /
|
||
cache-read / server-tool counts), `model`, `requestId`,
|
||
timestamps, every `tool_use` with its input, every `tool_result`
|
||
with the authoritative `is_error` flag, skill invocations,
|
||
slash commands, and a full separate transcript + `agentType` /
|
||
`description` for each subagent. Workflow runs additionally carry a
|
||
per-agent summary (stage label, model, a live token counter) in
|
||
`workflows/wf_*.json`; the counter under-tracks vs. the deduped raw
|
||
transcripts (~11% in the corpus behind issue #28), so the script
|
||
treats the raw transcripts as authoritative and reports the counter
|
||
only as `reported_total_tokens` for cross-checking.
|
||
- **Absent:** `costUSD` is always `null`; `durationMs` is always
|
||
`null`. There is no metered cost in the log, so this skill does
|
||
not compute one — it reports **raw token counts** (kept split by
|
||
class: input / output / cache-creation / cache-read, plus a
|
||
`total_tokens` sum). **Wall-clock is derived** from timestamp
|
||
deltas and the report says so.
|
||
|
||
The streamed transcript repeats the same `requestId` across
|
||
several assistant lines with an identical `usage` object. The
|
||
helper script dedups per `requestId`; never sum the raw lines.
|
||
|
||
## The Process
|
||
|
||
### Step 1 — Run the aggregator
|
||
|
||
```
|
||
python3 <skill-dir>/scripts/postmortem.py [--session <id>] [--cwd <project>]
|
||
```
|
||
|
||
Defaults to the newest transcript in the current project's log
|
||
dir — i.e. the session you are in (it will be flagged `active`,
|
||
meaning totals are partial). To grade a finished run, pass its
|
||
`--session <uuid>`.
|
||
|
||
The default log dir is derived from cwd (the path with `/` replaced
|
||
by `-`), so a session run inside a git worktree resolves to a
|
||
*different* slug than the primary clone, and after exiting a worktree
|
||
the default again follows the current cwd. To grade a worktree run
|
||
from the primary clone, pass `--project-dir
|
||
~/.claude/projects/<worktree-slug>` (or `--file <path>` to bypass slug
|
||
derivation entirely).
|
||
|
||
The script emits one JSON object on stdout. That JSON — not the
|
||
raw log — is your evidence base.
|
||
|
||
### Step 2 — Read the JSON and grade three axes
|
||
|
||
Grade each axis on the cited numbers. A grade with no number
|
||
behind it is an opinion; drop it.
|
||
|
||
1. **Token spend & efficiency** — `totals.total_tokens`, the
|
||
main-vs-subagent split, the token breakdown (output share is the
|
||
write-heavy signal), `cache_hit_ratio` (the dominant efficiency
|
||
lever: below ~0.7 on a long session means cache-cold turns
|
||
re-sending the full input), `server_tools` (web_search /
|
||
web_fetch counts).
|
||
2. **Toolchain health** — `tools.error_ratio` and `interrupted`
|
||
(the authoritative `is_error` signal), the tool mix (a Read /
|
||
Edit / Bash imbalance hints at thrashing or re-reading),
|
||
`slash_commands` such as `/compact` (context pressure).
|
||
3. **Agent effectiveness** — per subagent: `terminal_status`
|
||
(`DONE` / `DONE_WITH_CONCERNS` / `PARTIAL` / `BLOCKED` /
|
||
`NEEDS_CONTEXT` / `unknown`), `total_tokens`, and `requests`. A
|
||
fleet that burned real tokens to return `BLOCKED` is the headline
|
||
finding. Compare each agent's token spend to what it delivered.
|
||
For Workflow runs, grade per stage instead: `workflow_totals` and
|
||
each run's `stages` table (label-prefix grouping, e.g. all
|
||
`impl:N` under `impl`) show where the run's spend concentrated;
|
||
`agents_untracked > 0` flags retried/dropped attempts. Schema-bound
|
||
workflow stages carry no `terminal_status` — the workflow script's
|
||
own aggregation already judged them.
|
||
|
||
### Step 3 — Write the report
|
||
|
||
Write a Markdown report to the configured destination (see
|
||
**Output destination**). Structure it as **Output format** below.
|
||
Lead with the scorecard, support every grade with a number from
|
||
the JSON, and keep the prose tight — this is a retrospective, not
|
||
a transcript replay.
|
||
|
||
### Step 4 — Surface, don't fix
|
||
|
||
Translate the sharpest findings into recommendations. Where a
|
||
finding is a concrete, trackable defect (a subagent that reliably
|
||
bounces `BLOCKED`, a tool loop with a runaway error rate),
|
||
recommend filing it via the `issue` skill — name it, but do not
|
||
file it yourself unless the user asks. This skill ends at the
|
||
report.
|
||
|
||
## Output destination
|
||
|
||
Resolve in this order:
|
||
|
||
1. `--out <path>` if the invoker passed one.
|
||
2. The project's post-mortem directory, if it has one (its CLAUDE.md
|
||
project facts).
|
||
3. Default: `docs/postmortems/<session-id-short>-<yyyy-mm-dd>.md`
|
||
under the project root, creating the directory if needed.
|
||
|
||
Also print a ≤200-word summary to the chat so the headline grade
|
||
and token spend are visible without opening the file.
|
||
|
||
## Output format
|
||
|
||
```
|
||
# Post-mortem — <session-id-short> (<date>)
|
||
|
||
**Scorecard**
|
||
| Axis | Grade | Headline metric |
|
||
|------|-------|-----------------|
|
||
| Token spend & efficiency | A–F | <total_tokens> total, cache hit <ratio> |
|
||
| Toolchain health | A–F | <error_ratio>, <n> interrupts |
|
||
| Agent effectiveness | A–F | <k>/<n> agents DONE, <tokens> on subagents |
|
||
|
||
**Token spend**
|
||
<main vs subagent split; token breakdown by class (input / output /
|
||
cache-creation / cache-read); the one class that dominates and why>
|
||
|
||
**Toolchain**
|
||
<tool mix, error ratio + what the errors were, context-pressure
|
||
signals like /compact>
|
||
|
||
**Agents**
|
||
<per-subagent line: type — status — <total_tokens> — one-clause
|
||
verdict. Omit if no subagents were dispatched.>
|
||
|
||
**Recommendations**
|
||
<≤5 bullets. Each ties to a cited metric. Mark any that warrant
|
||
an `issue`.>
|
||
|
||
_Token counts are raw usage. Wall-clock is derived from timestamp
|
||
deltas, not metered._
|
||
```
|
||
|
||
If the session is flagged `active`, add one line at the top:
|
||
"⚠️ Session still active — figures are partial."
|
||
|
||
## Common Rationalisations
|
||
|
||
| Excuse | Reality |
|
||
|--------|---------|
|
||
| "I'll just skim the transcript and tally the tokens" | The transcript repeats usage per streamed line under a shared requestId. Hand-summing double-counts. Run the script. |
|
||
| "Let me convert the tokens to a rough dollar figure" | Don't. There is no metered cost in the log and prices vary by plan; report raw token counts and let the reader price them if they want. |
|
||
| "Cache hit ratio of 0.5 is fine" | On a long session it means half the input was re-sent uncached. That is the single biggest efficiency lever — grade it. |
|
||
| "All agents returned, so effectiveness is an A" | "Returned" ≠ "succeeded". A `BLOCKED` agent that burned tens of thousands of tokens is a failure that still spent the budget. Read `terminal_status`. |
|
||
| "Let me also peek at the IONOS token to check the endpoint" | Never. Secret files are out of bounds — see the Iron Law and the user's global rules. |
|
||
| "I found a broken agent, I'll fix its prompt now" | Out of scope. This skill reports and recommends; the fix is a separate, tracked change. |
|
||
|
||
## Red Flags — STOP
|
||
|
||
- Quoting any token number that did not come out of the script's
|
||
JSON.
|
||
- Converting token counts into a cost or dollar figure — report
|
||
raw counts only.
|
||
- Reading any file under `~/.claude` other than the session
|
||
transcript and its subagent logs — and never any credential or
|
||
token file.
|
||
- Editing code, baselines, or pipeline config "while you're in
|
||
there". Report only.
|
||
- Grading an axis with adjectives instead of the cited metric.
|
||
|
||
## Cross-references
|
||
|
||
- **Helper script:** `scripts/postmortem.py` — the deterministic
|
||
aggregator. All numbers originate here.
|
||
- **Hand-off target:** the `issue` skill, when a finding is a
|
||
trackable defect worth filing.
|
||
- **Report destination (optional):** the project's post-mortem
|
||
directory, if its CLAUDE.md project facts name one.
|
||
- **Data-source note:** this skill reads only the current
|
||
session's own transcript and subagent logs. It does not crawl
|
||
other projects or other sessions (single-session by design).
|