| name | walkthrough |
| description | Generate a narrative walkthrough of code changes with difftastic diffs and verified complete coverage. Also works as a general markdown renderer with source blocks, tables, and mermaid diagrams. |
Walkthrough Generator
Generate a narrative walkthrough of code changes, rendered as an HTML document with
side-by-side difftastic diffs. Every change chunk must be referenced in the walkthrough,
ensuring complete coverage. The narrative goes through an adversarial review loop to
ensure accuracy and clarity.
The renderer also works as a general-purpose markdown-to-HTML tool without any diff
data. Use it for technical documents, design docs, or explanations that benefit from the
walkthrough styling (tables, mermaid diagrams, source blocks with syntax highlighting).
To render plain markdown:
walkthrough render doc.md --no-diff-data -o doc.html
Arguments
Parse the user's invocation arguments to determine the mode:
Diff walkthrough mode (default)
- No args / on a branch:
walkthrough collect with no diff args. The tool auto-detects
the current branch, finds the merge-base with origin/master (or origin/main), and diffs
only the branch's changes. This is the preferred mode when on a feature branch. Do
NOT manually construct diff ranges when on a branch.
staged or --cached: staged changes (git diff --cached)
- A commit SHA (e.g.
abc123): that commit (git diff abc123~1 abc123)
- A range
A..B: that range (git diff A..B)
HEAD~N: last N commits (git diff HEAD~N HEAD)
--output <path>: output path for the walkthrough markdown (default: walkthrough-{timestamp}.md)
Derive two values from this:
DIFF_ARGS: the arguments to pass after -- to walkthrough collect. Leave empty
when on a branch (the tool handles merge-base detection automatically). Only set this
for explicit overrides like --cached or specific commit ranges.
OUTPUT_PATH: where to write the walkthrough markdown
Plain markdown mode
- A path to a
.md file that doesn't require diff data: render it directly.
Skip Steps 2-4 (collect, summary, write narrative) and go straight to Step 5 (render).
Use --no-diff-data to skip loading diff data and omit the coverage badge.
Step 1: Check prerequisites
Run which difft. If not found, ask the user if they want to install via brew install difftastic.
If they decline, stop with: "difft is required for walkthrough generation."
Verify the version supports JSON output (0.67.0+): difft --version.
Run which walkthrough. If not found, install it:
cargo install --path ~/code/walkthrough
Step 2: Collect diffs
First, check if you're on a feature branch:
git branch --show-current
If on a feature branch (not master/main), run collect with no diff args:
walkthrough collect -o .walkthrough_data/
The tool auto-detects the merge-base with origin/master (or origin/main) and diffs only
the branch's changes. This is reliable even when the local master ref is stale or the
branch has been rebased.
Only pass explicit diff args for special cases (staged changes, specific commit ranges):
walkthrough collect -o .walkthrough_data/ -- $DIFF_ARGS
This produces JSON files for the renderer and a SUMMARY.md in the data directory.
Do not read the JSON files. They contain machine-readable token spans meant for the
renderer.
Step 3: Read the summary and plan
Copy .walkthrough_data/SUMMARY.md to OUTPUT_PATH:
cp .walkthrough_data/SUMMARY.md "$OUTPUT_PATH"
Read OUTPUT_PATH. It is already a valid walkthrough markdown file with difft code blocks
containing text diffs for every file and chunk. Each block has the correct chunks= spec
and the text diff body shows exactly what changed. HTML comments note chunk indices and
new-file line ranges.
Plan how to reorganize this into a narrative. Group by theme, not by file:
- Core logic changes: chunks with substantive behavior changes. Lead with these.
- New modules/files: introduce new concepts early, before their usage sites.
- Refactoring/renames: mechanical changes grouped together.
- Import/config updates: boilerplate changes, put these last.
- Test changes: group test updates near the code they test, or in a separate section.
If a chunk contains multiple logical changes, use lines=START-END to split it.
Step 4: Write the walkthrough narrative
Edit OUTPUT_PATH to restructure the summary into a narrative. The difft code blocks are
already there, so focus on:
- Replace the
TODO title and overview
- Reorganize sections by theme (move, merge, or split difft blocks as needed)
- Add prose before each difft block explaining what the diff shows and why
- Number the sections
Code block rules
-
Every code block that references diffs uses the info string format:
difft <file-path> chunks=<spec> where spec is comma-separated indices or all.
Optionally add lines=START-END (1-based, inclusive) to show only a portion of the
chunk. This is useful for splitting a large chunk (e.g. two new functions in one chunk)
across multiple sections with prose in between.
-
The block body will be populated by the render step with a unified-diff-style text
representation ( context, - removed, + added). You do not need to reconstruct
code manually from the JSON.
-
Group by narrative, not by file. A single section can reference chunks from multiple
files. A single file's chunks can appear across multiple sections.
-
The same file can appear multiple times in different sections with different chunk
selections.
-
Use chunks=all for new files, deleted files, or files with only one or two chunks.
Syntax-highlighted code blocks
Fenced code blocks with a language tag are syntax-highlighted via tree-sitter. Always
specify the language. Use plain for blocks with no syntax highlighting.
```typescript
const ws = await createWorkspace(context, { bootstrap: config });
```
```plain
some output with no highlighting
```
Supported languages include: typescript/ts, javascript/js, tsx, jsx, rust/rs, python/py,
go, ruby/rb, java, c, cpp, css, html, json, yaml, toml, bash/sh/shell, sql, swift, kotlin/kt.
Task lists
Markdown task lists (- [x] and - [ ]) render as styled checkboxes. Use them in
overviews or summaries to show progress or completion status.
Source blocks
Use ```src code blocks to show existing code for context alongside diffs. This is
useful when the narrative needs to show the old implementation being replaced, or related
code that helps the reader understand what the diff is changing.
```src services/cortex/lib/support/foundry_api.ts:128-159
```
```src services/cortex/lib/support/foundry_api.ts:128-159 old
```
The syntax is src <filepath>:<start>-<end> [old] where start/end are 1-based line numbers.
Adding old shows the pre-change version of the file (the header displays "(old)" to
distinguish it). Without old, the new (post-change) version is shown.
The renderer pulls lines from the collected difft JSON data, syntax highlights them, and
renders as a single-column code block with the same styling as diff blocks.
Use source blocks to:
- Show the old implementation being replaced by new code
- Show related code that provides context for the change
- Show configuration or type definitions referenced by the diff
Prefer showing code over describing code. When new code is a port or restructuring of
existing code, show the old code with a src block and let the reader see the 1:1
correspondence themselves. A sentence like "The config is a direct port of the existing
sandboxEnvVars workload block" followed by the old code is far clearer than a paragraph
explaining each field mapping in prose. The diff already shows the new code; the src
block shows what it replaced.
Service badges
Use `@service-name` in inline code to render service names as styled badges
(orange text, light background, heavier font weight). This makes services stand out
from regular code references in the prose.
Examples:
`@sboxd` renders as a badge for the sboxd service
`@cortex` renders as a badge for cortex
`@nimbus` renders as a badge for nimbus
Use service badges for:
- Service/process names (
@sboxd, @cortex, @nimbus)
- Infrastructure components (
@ugit, @foundry)
- External systems the code interacts with
Do NOT use for:
- Type names, function names, or field names (use regular
`code` for those)
- Feature flag names (use regular
`code`)
Mermaid diagrams
Use ```mermaid code blocks to add diagrams that visually explain the change. The
renderer pre-renders them to inline SVG via mmdc (no JavaScript dependency in the HTML).
Diagrams should show high-level architecture, not restate code. A diagram that mirrors
an if/else branch from the diff adds no value. Instead, diagrams should help the reader
understand the system-level context that the code operates in.
The most effective pattern is before/after sequence diagrams that show how the
interaction between services changes. Place one in the overview section to orient the
reader before they see any code:
```mermaid
sequenceDiagram
participant Cortex
participant Nimbus
participant Sboxd
rect rgb(240,240,240)
Note over Cortex,Sboxd: Before: nimbus mediates bootstrap
Cortex->>Nimbus: POST /sandbox {flat envVars}
Nimbus->>Sboxd: POST /api/ready {envVars}
Nimbus-->>Cortex: sandbox URLs
end
rect rgb(230,245,230)
Note over Cortex,Sboxd: After: cortex bootstraps directly
Cortex->>Nimbus: POST /sandbox
Nimbus-->>Cortex: sandbox URLs
Cortex->>Sboxd: workspace/create {BootstrapConfig}
end
```
Other useful diagram types:
- Sequence diagrams for request flows, RPC call chains, or multi-step processes
- Class diagrams for type relationships when new types are introduced
Do NOT use:
- Flowcharts that restate branching logic already visible in the diff (if/else,
feature flag checks, guard conditions). The code is the source of truth for control
flow; a flowchart just adds a less precise duplicate.
Place diagrams near the top of the walkthrough to orient the reader before the diff
details. Keep them simple (5-10 nodes max). If mmdc is not installed, the renderer
falls back to showing the mermaid source in a code block.
Prose style
-
Lead with WHY, not WHAT. The diff shows what changed. The prose should explain the
motivation, the constraint that forced this design, or the problem it solves. A reader
should understand the reasoning before seeing the code. Bad: "This adds a timeout
parameter." Good: "WebSocket connections can hang indefinitely if sboxd is unresponsive,
so we add a 30-second timeout."
-
Be concrete and specific. Replace vague descriptions with exact names, values, and
relationships. Bad: "This updates the config." Good: "This adds bootstrap?: BootstrapConfig
to the WorkspaceCreateRequest wire type, so the TS client can send structured bootstrap
data over WebSocket." Reference function names, type names, and field names by their actual
identifiers.
-
Explain terms inline, not upfront. Do not create a glossary or "key terms" section.
Instead, define domain-specific terms, service names, and jargon the first time they
naturally appear in the narrative. Anchor explanations with concrete use-cases or examples
when possible (e.g. "a sandbox is an isolated container where user code runs; each
Figma file gets its own"). Assume the reader may be unfamiliar with the codebase.
-
Interleave prose and diffs. When a section has multiple diffs, place explanatory text
between them rather than grouping all prose at the top and all diffs at the bottom. Each
diff block should be immediately preceded by the prose that explains it.
-
Use present tense. "This extracts..." not "This extracted...". The walkthrough
describes the change as it exists now.
-
Keep it tight. One to three sentences per diff block is usually enough. If you need
more, the section should probably be split. Avoid restating what the diff already shows
(e.g. don't list every field that was added if the diff is self-evident). Focus on the
non-obvious: why this approach, what trade-off was made, what edge case it handles.
But only assert a WHY you can actually source — from the diff, the PR description,
commit messages, or surrounding code. Don't invent a plausible-sounding rationale or
mechanism to fill the gap; a fabricated "why" reads as authoritative and is worse than
omitting it. If the motivation isn't evident, either leave it out or hedge explicitly
("likely", "appears to"). The adversary reviewer flags fabricated rationale (check 7).
-
Connect sections. Brief transitions help the reader follow the thread. "With the
types in place, the client can now..." is better than an abrupt jump to the next section.
-
Title and overview matter. The title should be a concise summary of the change
(not a file name). The overview paragraph should give enough context that someone can
decide whether to read the full walkthrough.
Step 5: Render and enrich
Run the render command:
walkthrough render "$OUTPUT_PATH" --data-dir .walkthrough_data/ -o "${OUTPUT_PATH%.md}.html"
This does three things:
- Produces the HTML file with side-by-side diffs
- Writes text diff representations back into each difft code block in the markdown file
- Verifies coverage and adds a badge below the title showing whether all chunks are covered
If the render output reports uncovered chunks, add sections referencing them and re-render.
Handling stale-data errors
walkthrough render fails fast (non-zero exit) if HEAD has moved since collect
ran — e.g. a lint commit, amend, or rebase landed while you were writing the narrative
or iterating in the review loop. The error looks like:
Error: collected data is from HEAD <a> but current HEAD is <b>. Re-run `walkthrough
collect`, or pass --recollect to re-collect automatically, or --allow-stale ...
This guard exists because the collected chunk data no longer matches the working tree;
rendering it produces a diff that doesn't reflect the current code. Do NOT silence it
with --allow-stale — that ships the exact mismatch the guard prevents. Reconcile
instead:
- Re-collect:
walkthrough collect -o .walkthrough_data/ (re-detects the branch diff).
- Re-verify the narrative's chunk references, since re-collecting can renumber chunks:
walkthrough verify "$OUTPUT_PATH" --data-dir .walkthrough_data/
- Fix any
chunks= specs that verify flags as uncovered or out of range (a changed
diff may have split, merged, or shifted chunks), then re-render.
render --recollect folds step 1 into the render, but you must still run verify
afterward to catch shifted references — so prefer the explicit re-collect + verify
sequence above when this fires mid-walkthrough.
Step 6: Review loop
The review loop ensures the narrative is accurate, complete, and clear. Use the host's
available subagent mechanism to run three independent reviewer agents in parallel. Do not
pin a vendor-specific model name. If the host cannot run subagents, perform the three reviews
sequentially with fresh passes over the rendered markdown. Each review produces a verdict,
and the loop continues until all reviewers pass.
Budget: Maximum 3 review iterations. If reviewers are still failing after 3 rounds,
present the current state to the user and ask whether to continue or stop.
Reviewer 1: Adversary
Give one independent general-purpose reviewer this prompt:
You are an adversarial reviewer for a code walkthrough. Read the walkthrough
markdown file at {OUTPUT_PATH}.
The file contains prose sections interleaved with difft code blocks. Each code
block shows the actual diff (` ` context, `-` removed, `+` added lines). Your
job is to verify the prose accurately describes the diffs.
Check each section for:
1. **Prose/diff mismatch** — the prose claims something the diff doesn't show,
or misses something significant the diff does show. Read the diff line by
line and compare against the prose description.
2. **Inaccurate descriptions** — wrong function names, wrong field names, wrong
direction of change (says "adds" when the diff removes), wrong file
referenced.
3. **Missing context** — the prose doesn't explain WHY a change was made, only
WHAT changed. The reader should understand the motivation.
4. **Vague language** — handwavy descriptions where the diff has specific
details. "Updates the config" when the diff shows exactly which fields were
added.
5. **Ordering issues** — a section references concepts or types that haven't
been introduced yet. The narrative should flow so each section builds on
what came before.
6. **Overclaiming** — prose that states implications or consequences not evident
from the code (e.g. "this improves performance" when the diff just
restructures code).
7. **Fabricated rationale** — invented causal/mechanism explanations for WHY a
change was made or HOW something works, not grounded in the diff, PR
description, commit messages, or surrounding code (e.g. "the browser derived
it from the URL pattern" when nothing in the change shows that). This is the
most dangerous failure because item 3 actively asks for the WHY — a plausible
but unsupported "why" reads as authoritative. Flag any causal claim you can't
trace to concrete evidence. The fix is to either ground it in a citable source
or hedge it explicitly ("likely", "appears to") — not to assert it.
Write your findings as a numbered list. For each issue, cite the section number,
the specific prose, and what the diff actually shows.
End with a verdict:
- PASS — no issues found
- FAIL — {N} issues need to be addressed
Output ONLY the review, no preamble.
Reviewer 2: Open Questions
Give one independent general-purpose reviewer this prompt:
You are a completeness reviewer for a code walkthrough. Read the walkthrough
markdown file at {OUTPUT_PATH}.
The file contains prose sections interleaved with difft code blocks. Each code
block has a `chunks=` spec referencing specific diff chunks for a file. Your job
is to identify gaps and unanswered questions a reader would have.
Check for:
1. **Unexplained design decisions** — the code makes a choice (e.g. a specific
timeout value, a particular data structure, a feature flag name) but the
prose doesn't explain why.
2. **Missing error handling context** — the diff shows error handling, retries,
or fallbacks but the prose doesn't explain what failures they guard against.
3. **Unclear scope** — the reader can't tell what's in vs out of scope for this
change. Are there follow-up changes expected? Does this replace something?
4. **Undefined terms** — domain-specific jargon, service names, or acronyms
used without explanation on first appearance.
5. **Missing connections** — chunks from different files that are related but
the prose doesn't explain how they connect (e.g. a type definition in one
file used by a function in another).
6. **Reader confusion points** — places where a reader unfamiliar with the
codebase would be lost. Ask yourself: "If I didn't know this codebase,
would I understand what this section is telling me?"
Write your findings as a numbered list. For each issue, cite the section and
explain what question remains unanswered.
End with a verdict:
- PASS — no significant gaps
- FAIL — {N} issues need to be addressed
Output ONLY the review, no preamble.
Reviewer 3: Simplicity
Give one independent general-purpose reviewer this prompt:
You are a simplicity reviewer for a code walkthrough. Read the walkthrough
markdown file at {OUTPUT_PATH}.
The file contains prose sections interleaved with difft code blocks and
optional `notes` blocks (code annotations). Your job is to find prose that
restates what the code already says, and suggest removing it entirely.
Check each section for:
1. **Redundant explanations** — prose that describes what specific lines of
code do, when the code is self-evident. Example: "A 30-second timeout guards
against sboxd being unresponsive" is redundant when the code shows
`setTimeout(() => reject(...), 30_000)`. Remove them entirely.
2. **Paragraph-as-code-tour** — multiple sentences walking through the code
line by line. The reader can read the code. Focus prose on WHY, not WHAT.
Suggest which sentences to keep (the ones with non-obvious context) and
which to cut.
3. **Overly detailed field-by-field descriptions** — listing every field in a
config or type when the diff shows them. Suggest using a `src` block to
show related old code instead, or just letting the diff speak for itself.
4. **Annotations that should be prose** — `notes` blocks containing context
or motivation that would be better as a prose paragraph before the diff
(annotations are for what, prose is for why).
For each issue, suggest a specific fix:
- "Convert to annotation: `32-34: 30s timeout for unresponsive sboxd`"
- "Remove: code is self-evident"
- "Replace with `src` block showing old implementation"
- "Move to prose paragraph: this is motivation, not a code comment"
End with a verdict:
- PASS — prose is concise and complements the code
- FAIL — {N} issues need to be addressed
Output ONLY the review, no preamble.
Processing review results
After all three reviewers complete:
- If all PASS: proceed to Step 7.
- If any FAIL: triage and address the findings (see below), then re-run the
render command and run all reviewers again. Increment the iteration counter.
- After 3 iterations without all passing: present the remaining issues to
the user and ask whether to continue iterating or accept the current state.
Triage: classify each issue before editing
For each reviewer issue, classify it as one of:
- add-to-overview: domain context or definitions that belong in the introduction
- add-annotation: a brief note on a specific line (use
notes block)
- remove-prose: code-touring or redundant text to cut
- out-of-scope: rabbit-hole questions about the broader codebase that aren't about
this change's design (e.g. "explain the SequenceNumber numbering scheme" when the
change is about a race fix)
Address Simplicity removals first (cut the cruft), then add Open Questions context
into the cleaned-up space. This prevents adding new prose that Simplicity immediately
flags as code-touring. Prefer adding context to the overview or as annotations rather
than as new paragraphs next to diffs.
Preventing reviewer recursion on iteration 2+
On subsequent iterations, include the previous iteration's feedback in each reviewer's
prompt by appending:
IMPORTANT: The following issues were raised in the previous iteration and have
been addressed in the current version. Do NOT re-flag these issues or ask for
deeper explanations of concepts that are now defined. Only flag genuinely NEW
issues or issues that were NOT addressed at all.
Previous iteration feedback:
{previous_feedback}
This prevents the pattern where Open Questions asks "explain X", the walkthrough adds
an explanation, and the next iteration asks "explain X more deeply." Once a concept is
defined, the reviewer should accept the definition and move on.
Step 7: Present
Open the HTML file:
open "${OUTPUT_PATH%.md}.html"
Print a summary:
- Number of files covered
- Number of chunks covered
- Review iterations completed
- Output file path
If the user asks to publish, run:
walkthrough publish "${OUTPUT_PATH%.md}.html"