---
name: performance-metrics
description: Log task completion data to .claude/metrics/. Use at the end of every task to record tokens, cost, agents used, rework cycles, and hallucination events. Also use for periodic reporting to identify efficiency and quality trends.
role: worker
user-invocable: true
---

# Performance Metrics

## Overview

Schema and procedures for capturing performance data in `.claude/metrics/`. Metrics enable evidence-based evaluation of agent effectiveness, cost efficiency, and quality outcomes.

## Constraints

- Never log credentials, API keys, or PII in metric entries
- Log entries are append-only; do not modify or delete existing JSONL records
- Log at task completion, not mid-task; mid-task state belongs in `.claude/memory/` progress files
- Use the defined JSONL schema; do not invent new top-level fields without updating the reference

## Metric Categories

> **Targets discipline.** Per CLAUDE.md → "Claims discipline", a numeric target
> may only ship if an instrument measures it. The instrumented metrics below cite
> their sensor; the rest read "Aspirational — no sensor yet" until one exists
> (tracked by #102 cost metering and #106 telemetry). Do not reintroduce bare
> numeric targets — `tests/docs/prose_honesty_test.bats` enforces this across all
> shipped prose.

### Efficiency Metrics

| Metric | Description | Target |
| --- | --- | --- |
| Task completion time | Wall-clock time from request to delivery | Track trend, no fixed target |
| Token usage per task | Total input + output tokens consumed | Minimize for comparable quality |
| Agent loading overhead | Tokens spent on agent/skill file reads | Aspirational — no sensor yet |
| Context summarization frequency | How often summarization triggers per task | Aspirational — no sensor yet |

### Quality Metrics

| Metric | Description | Target |
| --- | --- | --- |
| First-pass acceptance rate | Tasks accepted without rework | Aspirational — no sensor yet |
| Rework count | Number of revision cycles per task | Aspirational — no sensor yet |
| Hallucination incidents | Outputs containing fabricated information | Aspirational — no sensor yet |
| Accuracy score | Correctness of structured data extraction | Aspirational — no sensor yet |
| Test coverage | Percentage of code covered by generated tests | Track per project |

### Cost Metrics

| Metric | Description | Target |
| --- | --- | --- |
| Cost per task | Total API cost (input + output tokens at rate) | Track trend |
| LLM routing ratio | Percentage of tasks routed to each LLM | Track distribution |
| Selective loading savings | Tokens saved vs. loading all agents | Aspirational — no sensor yet |

## Log Format

Metrics are stored in `.claude/metrics/` as JSONL files (one JSON object per line).

### File Naming

```
.claude/metrics/{date}-task-log.jsonl
```

Example: `.claude/metrics/2026-02-20-task-log.jsonl`

### Task Completion Entry

Logged at the end of each task:

```json
{
  "timestamp": "2026-02-20T14:30:00Z",
  "task_id": "unique-id",
  "task_type": "implementation",
  "task_description": "Build REST API for user authentication",
  "agents_used": ["software-engineer", "architect"],
  "skills_used": ["hexagonal-architecture"],
  "tokens": {
    "input": 12500,
    "output": 3200,
    "total": 15700
  },
  "cost_usd": 0.043,
  "llm": "opus",
  "handoffs": 0,
  "phases": 2,
  "rework_cycles": 1,
  "accepted": true,
  "hallucination_detected": false,
  "duration_seconds": 180
}
```

### Field Reference

| Field | Type | Description |
| --- | --- | --- |
| `timestamp` | string | ISO 8601 completion time |
| `task_id` | string | Unique identifier for this task |
| `task_type` | string | `implementation`, `design`, `bugfix`, `testing`, `documentation`, `analysis` |
| `task_description` | string | Brief description of the task |
| `agents_used` | string[] | Agent names that were loaded |
| `skills_used` | string[] | Skill names that were loaded |
| `tokens.input` | number | Input tokens from API usage field |
| `tokens.output` | number | Output tokens from API usage field |
| `tokens.total` | number | Sum of input + output |
| `cost_usd` | number | Estimated cost based on token rates |
| `llm` | string | Model ID used |
| `handoffs` | number | Times the handoff skill was triggered (either mode) |
| `phases` | number | Number of loading phases |
| `rework_cycles` | number | Number of revision cycles |
| `accepted` | boolean | Whether the user accepted the output |
| `hallucination_detected` | boolean | Whether a hallucination was flagged |
| `duration_seconds` | number | Wall-clock seconds from start to delivery |

### Cost Metering Entry (#102, #1094)

The `Stop`/`SubagentStop` hook (`hooks/cost_meter.py` →
`hooks/lib/cost_meter.py record`) appends one entry per fire to
`.claude/metrics/cost-metering.jsonl` — the running per-session token/cost summary
parsed from the transcript. Full schema reference:
`knowledge/telemetry-schema.md`.

```json
{
  "timestamp": "2026-07-18T14:30:00Z",
  "transcript": "session.jsonl",
  "total": {"input_tokens": 18000, "output_tokens": 3500, "cost_usd": 0.16, "messages": 12},
  "by_model": {"<model-id>": {"cost_usd": 0.16, "input_tokens": 18000, "output_tokens": 3500}},
  "by_thread": {"main": {"cost_usd": 0.10, "input_tokens": 10000, "output_tokens": 2000},
                "subagent": {"cost_usd": 0.06, "input_tokens": 8000, "output_tokens": 1500}},
  "by_agent_type": {"main": {"cost_usd": 0.10, "input_tokens": 10000, "output_tokens": 2000},
                    "security-review": {"cost_usd": 0.06, "input_tokens": 8000, "output_tokens": 1500}}
}
```

| Field | Type | Description |
| --- | --- | --- |
| `total` | object | Session-cumulative token counts, `cost_usd`, `messages` |
| `by_model` | object | Per-model slim breakdown (`cost_usd`, `input_tokens`, `output_tokens`) |
| `by_thread` | object | `main` vs `subagent`, from the native `isSidechain` flag |
| `by_agent_type` | object | `main` for main-loop turns; sidechain turns keyed by subagent type via the harness-recorded `attributionAgent` field or the Task-dispatch `subagent_type`/`agentId` join; unmappable sidechain spend lands in `unattributed` (#1094) |

**Privacy:** token counts, dollar amounts, model identifiers, and thread/
agent-type identifiers only — never prompt text, code, file paths, or tool
payloads. Attribution reads only fields the Claude Code harness itself writes
to the transcript; the meter never guesses (see #170 for the buckets removed
because the harness records no signal for them). Disable with
`DEV_TEAM_COST_METER=off`. Report it with `/cost-report`.

### Review Value Entry (#348)

`/build` appends one entry per **inline review checkpoint** to
`.claude/metrics/review-value.jsonl` so the pipeline's review overhead becomes
*measurable* — distinguishing a build where review caught and fixed a real defect
from one where every loop passed no-op. This is the sensor that lets the plan/step
tiering (the `/plan` plan-tier and `/build` per-step complexity routing) be
right-sized with evidence rather than guessed.

```json
{
  "timestamp": "2026-06-22T14:30:00Z",
  "plan": "plans/add-auth.md",
  "slice": "2",
  "step": "all",
  "checkpoint": "slice",
  "complexity": "standard",
  "source": "build-checkpoint",
  "agents_run": ["spec-compliance-review", "security-review"],
  "issues_found": 1,
  "severity_breakdown": {"errors": 1, "warnings": 0, "suggestions": 0},
  "issues_fixed": 1,
  "fix_iterations": 1,
  "outcome": "fixed"
}
```

| Field | Type | Description |
| --- | --- | --- |
| `checkpoint` | string | `step` (per-step `complex` review) or `slice` (batched slice-boundary review) |
| `source` | string | Row provenance — `build-checkpoint` (fix-applying `/build` checkpoint, the default when absent) or `code-review` (read-only standalone review). `/harness-audit` Step 4 excludes `code-review` rows from fix-rate drop-candidate logic (#1257) |
| `step` | string | `N.M` for a per-step checkpoint; `all` for a batched slice checkpoint |
| `agents_run` | string[] | Review agents and static-analysis lane tools run at this checkpoint |
| `issues_found` | number | Actionable issues the checkpoint surfaced (semantic review + static lanes) |
| `severity_breakdown` | object | `{errors, warnings, suggestions}` counts (same enum as `/code-review`); the three sum to `issues_found`. Lets `/harness-audit` Step 3 flag low-value (mostly-minor) lenses (#1256) |
| `issues_fixed` | number | Of those, how many were auto-fixed |
| `fix_iterations` | number | Review-fix and static self-heal fix-loop iterations consumed |
| `outcome` | string | `no-op` (passed clean), `fixed` (found + fixed), `escalated` (a fix loop didn't converge — including a static lane capping out) |

**Privacy:** counts and outcomes only — never prompt text, code, or file content,
consistent with the cost meter's privacy boundary. Disable with
`DEV_TEAM_REVIEW_VALUE=off`. Report it with `/cost-report` (its "review value"
section).

### Verify Log Entry (#727)

`/build` appends one entry per **slice with a runtime surface** to
`metrics/verify-log.jsonl` (sub-step 4.9) — evidence that the project's own
test/verification tooling actually exercised the change end-to-end before the
slice was marked done, or was explicitly skipped because the diff had no
runtime surface to drive. This is the sensor that closes the gap the #727
investigation found: a "done" feature that fails the first time it's really
run means runtime verification was either skipped or never mandated in the
first place.

```json
{
  "timestamp": "2026-07-02T14:30:00Z",
  "plan": "plans/add-auth.md",
  "slice": "2",
  "branch": "feat/add-auth",
  "files": ["src/api/auth.py"],
  "outcome": "ran",
  "reason": null
}
```

| Field | Type | Description |
| --- | --- | --- |
| `branch` | string | Current branch name — `${CLAUDE_PLUGIN_ROOT}/scripts/progress_guardian.py --pre-pr` matches on this |
| `files` | string[] | The slice's changed runtime files the verification was scoped to |
| `outcome` | string | `ran` (the verification ran and passed), `skipped` (no runtime surface — see `reason`), or `failed-then-fixed` (the verification failed at least once before the fix landed) |
| `reason` | string \| null | Required when `outcome` is `skipped` (e.g. `"tests-only"`, `"docs-only"`); `null` otherwise |

**Not disableable.** Unlike Review Value logging (`DEV_TEAM_REVIEW_VALUE=off`),
there is no env var to turn this off — `${CLAUDE_PLUGIN_ROOT}/scripts/progress_guardian.py --pre-pr`
fails closed on a branch with runtime-surface changes and no matching entry,
and that gate is a correctness control, not a metrics-collection nicety.

## When to Log

> **Task completion entries are written automatically** by `hooks/task_completion_metrics.py`
> on every `Stop` / `SubagentStop` event — skills no longer need a manual
> "log this on completion" step. To surface task-specific values (task type,
> agents used, hallucination flag, rework count, defect count, or a config
> change), populate `.claude/session-metrics.json` before the session ends; the
> hook reads and clears it. Leave the file absent for a minimal heartbeat entry.

| Event | Action |
| --- | --- |
| Task completed | Logged automatically by `hooks/task_completion_metrics.py` (no manual step needed) |
| `/build` inline review checkpoint | Append a Review Value entry to `.claude/metrics/review-value.jsonl` (#348) |
| `/build` slice with a runtime surface | Append a Verify Log entry to `metrics/verify-log.jsonl` (#727) |
| Configuration change | Add `config_change` to `.claude/session-metrics.json`; hook writes `.claude/metrics/config-changelog.jsonl` |
| Hallucination detected | Set `hallucination_detected: true` in `.claude/session-metrics.json` |
| Context summarization triggered | Increment counter in current task entry |

## Output

JSONL log entries written to `.claude/metrics/` and/or a summary report of metric trends. Be concise — report anomalies and trend signals; omit entries within normal range.

## Reporting

Periodically review metrics to identify patterns:

1. **Weekly**: Review task completion entries for rework trends and hallucination rate
2. **Monthly**: Aggregate cost metrics and LLM routing distribution
3. **Per-project**: Compare first-pass acceptance rate across task types

Summaries can be written to `.dev-team-reports/` for historical reference.
