# Explain Diff

> Translate a DriftReport JSON into a plain-English markdown narrative suitable for pasting into a PR description, chat thread, or bug report. Read-only template render — no LLM call, no filesystem mutation. Pairs with `doctor` via the `--explain` flag, or runs standalone against any DriftReport produced by `bin/compute-drift.sh` / `bin/retrofit.sh --json` / `bin/doctor.sh --json`. TRIGGER when the user says "explain the drift", "summarize doctor in plain English", "what does this drift report mean", "narrate the drift", "translate the drift report", "human-readable drift", "explain what nyann found", "make doctor output paste-friendly", "give me a drift summary for the PR", "/nyann:explain-diff". Do NOT trigger on "what's drifted" alone — that's `doctor` / `retrofit --report-only` (which produce the raw structured report this skill consumes). Do NOT trigger on "fix the drift" — that's `retrofit`.

- Skill: `thettwe/explain-diff` (Agent Skill)
- Install (CLI): `npx skillmds@latest add thettwe/explain-diff`
- Raw SKILL.md: https://api.skillmd.com/api/skills/thettwe/explain-diff/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: thettwe (https://skillmd.com/u/thettwe)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/thettwe/explain-diff

---


# explain-diff

Read-only template render. Wraps `bin/explain-diff.sh`.

## When to trigger

- User has a DriftReport (from doctor / retrofit / compute-drift) and wants prose.
- User wants to paste a doctor summary into a PR body or chat without sharing raw JSON.
- User explicitly asks for "the drift in plain English".

## When NOT to trigger

- User is asking "is this repo healthy" → that's `doctor` (which can chain into this via `--explain`).
- User wants to mutate / fix → that's `retrofit`.
- User wants LLM-polished output → this skill is template-only by design (no LLM). If the user wants prose with more nuance, suggest reading the markdown output and editing by hand.

## Invocation

Three call shapes:

**1. Direct: file path**
```
bin/explain-diff.sh --file path/to/drift.json
```

**2. Pipe from a producer**
```
bin/compute-drift.sh --target . --profile default | bin/explain-diff.sh -
```

**3. Via doctor (recommended path)**
```
bin/doctor.sh --target . --profile default --explain
```

`doctor --explain` is preferred because it also forwards the computed health score and trend delta into the narrative header line — the same numbers text-mode shows at the foot of its output.

`--explain` is mutually exclusive with `--json` on doctor; if both are passed, doctor dies with a clear error.

## Output shape

**Markdown (default)** — three sections:

1. **Header** — target + profile + (optional) health score + trend
2. **What's drifted** — one bullet per drift category, ordered by severity. Each bullet has a lead phrase (`Action required:`, `Worth fixing:`, `Drifted:`, `Minor:`) and up to 5 concrete items. If a category has more than 5 items, an "...and N more" line appears.
3. **What you can do** — ordered action items that map to nyann skills (`nyann:retrofit`, `nyann:optimize-claudemd`, etc.)

**JSON (`--format json`)** — the DriftNarrative shape (`schemas/drift-narrative.schema.json`). Useful for chat-bot integrations or custom renderers that want the same source-of-truth without re-parsing the prose.

## Severity → lead-phrase mapping

| Severity | Lead | Sources |
|---|---|---|
| critical | "Action required:" | `missing[]`, broken links, `claude_md=error` |
| high | "Worth fixing:" | `misconfigured[]`, `claude_md=warn` / `absent`, subsystem errors |
| medium | "Drifted:" | `orphans`, staleness, `misplaced[]` |
| low | "Minor:" | non-compliant history (informational; nyann doesn't rewrite history) |
| info | (suppressed in markdown) | reserved for downstream filtering via the JSON shape |

The mapping is intentionally fixed (no profile-level override) so a narrative pasted into a PR reads the same regardless of which profile produced the underlying report.

## Reading the output back to the user

When the user asks for "the drift in plain English":

- Print the markdown block as-is. It's already formatted for human consumption.
- If the user asks for a "one-line summary" instead, count the `## What's drifted` bullets — the section count IS the summary number. E.g. "3 categories drifted: hooks (critical), claude_md (high), history (informational)."
- If the user asks "should I worry about Minor items" → no, those are CC-style informational checks; nyann doesn't rewrite history.
- If the report is clean (no `## What's drifted` section appears), the narrative says so explicitly — don't editorialise.

## Limits

- Template-only. No LLM smoothing — accept slightly mechanical phrasing as the cost of offline-safe rendering.
- Per-section items capped at 5 in the markdown output; the full list is in the JSON shape if a consumer needs it.
- Health score / trend are optional — if the caller doesn't supply them, the score line is suppressed (not faked to zero).

