| name | report |
| description | Render a research, documentation, or analysis report as a designed HTML document using the project template, instead of emitting a raw `.md` file. Use whenever the user asks for a report, analysis, deep-dive, investigation, postmortem, summary, write-up, briefing, or any structured long-form (>500 words, multiple sections) output that would normally be a Markdown file. The user reads these in the browser as a designed document. Triggers: "report on X", "analyze X", "research X", "deep-dive on X", "investigate X", "summarise X for me", "write up X", "document X", "post-mortem", or any similar request where the natural output would be a multi-section Markdown document. Skip for: short conversational answers, single-file code edits, one-paragraph notes. |
Report skill
Convert agent research, documentation, code analysis, and technical write-ups into a designed HTML report instead of raw Markdown. The user reads these in the browser; the page handles light/dark theme, table of contents, footnotes, code blocks, callouts, framed figures with click-to-zoom, and one-click export back to Markdown.
When this triggers
Any time the natural output would be a .md file with headings, multiple sections, and more than ~500 words. Specifically:
- "Write me a report / deep-dive / summary / analysis on X"
- "Investigate X and document what you find"
- "Research X and give me your findings"
- "Postmortem the Y incident"
- "Document the Z system / architecture / decision"
- "Write up the conclusions from our discussion"
- "Compare X and Y in detail"
Skip this skill for: quick conversational answers, single-file code edits, status updates, one-line confirmations, anything under ~300 words. The user wants this for real reports, not for every response.
The workflow
- Write the report as Markdown with YAML frontmatter (see Frontmatter and Markdown syntax below). Save it to the project's
reports/ directory.
- Run the renderer:
node ~/.claude/skills/report/render.mjs reports/your-report.md
This produces reports/<title-slug>-<date>.html in the same directory.
- Tell the user where to open it. Provide the absolute file path; they open it in their browser.
The cost: the agent writes Markdown only. Token spend is identical to writing a plain .md file. The HTML is generated mechanically by the renderer.
Editing an existing report
When the user asks to update, edit, modify, or expand an existing report — do not re-write it from scratch. Edit the source Markdown and re-render. This preserves footnotes, tags, structure, and keeps the diff small.
Two cases:
Case A — the source .md still exists (it always does, if the report was rendered into reports/):
- Read
reports/<slug>-<date>.md (or whatever the source file is called).
- Apply the user's requested edit by editing the Markdown directly.
- Re-render:
node ~/.claude/skills/report/render.mjs reports/<slug>-<date>.md
The HTML at reports/<slug>-<date>.html is overwritten in place.
- Tell the user the report has been updated and point at the same HTML path. They refresh the browser tab.
Case B — only the .html exists (rare; the .md was deleted or the report came from elsewhere):
- Recover the source Markdown from the embedded
<script id="source-md"> block:
node ~/.claude/skills/report/extract.mjs reports/the-report.html
This writes reports/the-report.md next to the HTML. Pass --force to overwrite an existing .md.
- Edit the recovered
.md.
- Re-render with
render.mjs as in Case A.
Pinning the output filename. By default render.mjs derives the output filename from the title slug and date, which means changing the title or letting the date roll forward will produce a new HTML file. To keep the output path stable across edits, pass it explicitly:
node ~/.claude/skills/report/render.mjs reports/the-report.md reports/the-report.html
Use this when the user expects "the report" to live at a fixed URL or path across edits.
Do not hand-edit the rendered HTML. Edits made directly to the .html will be lost the next time the report is re-rendered, and the embedded source .md will go out of sync with the visible content. Always edit the Markdown.
Reviewing a rendered report with comments
Use the review workflow when the user says they want to comment on a rendered report, rerun with comments, apply report comments, revise the report from review notes, or approve the report as final.
Start a review session
The rendered HTML remains the working artifact. Start the local review server against the report:
node ~/.claude/skills/report/review.mjs reports/the-report.html
Open the printed localhost URL. In review mode, click a report block to add a comment. The server stores comments as @report-comment markers inside the embedded <script id="source-md" type="text/markdown"> Markdown block in the same HTML file.
Review comments attach to rendered blocks, not exact text ranges. The marker position in Markdown is authoritative; block:bN IDs are a UI aid for placing new comments.
Comment mode starts off. Use the Comment button or press C to turn it on; the banner confirms that block clicks will create new comments. Press Esc to leave comment mode, or Cmd+Enter / Ctrl+Enter to save the open dialog. Existing comments are managed from the review tray without entering comment mode.
The review tray is the primary comment-reading surface. It defaults to Open, shows open/resolved/total counts, filters comments, jumps to the target block, and supports edit, resolve, reopen, and delete actions. Press N to jump to the next open comment. Use #comment-cN links for a stable comment deep link and #block-bN links for a target block deep link.
To export a Markdown review report:
node ~/.claude/skills/report/review-summary.mjs reports/the-report.html
node ~/.claude/skills/report/review-summary.mjs reports/the-report.html reports/the-report-review-summary.md
Rerun with comments
When the user asks to rerun or revise using report comments:
- Read the reviewed HTML file.
- Extract the embedded Markdown:
node ~/.claude/skills/report/extract.mjs reports/the-report.html reports/the-report.md --force
- Find all
@report-comment markers with status="open".
- Revise only the affected block or section unless the comment explicitly asks for broader changes.
- Change each handled marker to
status="resolved".
- Add a concise
Resolved: paragraph under the original comment text.
- Re-render to the same HTML path:
node ~/.claude/skills/report/render.mjs reports/the-report.md reports/the-report.html
Keep resolved comments during active review. They are an audit trail for what changed and why.
Final approval cleanup
When the user approves the report as final, strip all review comments and re-render the same HTML path:
node ~/.claude/skills/report/clean-comments.mjs reports/the-report.html
Do not keep an audit copy unless the user explicitly asks for one.
Frontmatter
Every report starts with a YAML frontmatter block. Required fields are bold.
---
title: On the failure modes of LLM-generated frontend code
summary: A pattern catalogue from two hundred reviewed PRs.
generated_by: Claude Opus 4.7 (1M context)
date: 2026-05-05
status: draft
tags: [llm, frontend, design-systems]
sources: 14
version: 1
eyebrow: Research report · Frontend
The status: field is a closed set: draft, in-review, reviewed, final. The renderer validates and errors on anything else. Older reports without status: default to draft with a one-line stderr nudge — add the field next time you edit them.
The renderer auto-computes reading time from the rendered word count.
Markdown syntax
The renderer supports a focused subset of Markdown plus a handful of extensions specific to this template. Stick to what's listed below — anything else is unsupported.
Headings
# is the document title (taken from frontmatter title; do not write # in body). Use ## for top-level sections, ### for subsections, #### for small uppercase sub-headings. The renderer auto-generates IDs and anchor links.
## The audit
### Method
#### Training-data median
Section ledes
The first paragraph after a ## heading, if it is entirely italic, is rendered as a section lede (a slightly larger italic summary line). This is a convention; use it to give every section a one-line summary that lets the reader skim:
## The audit
*Two hundred merged or rejected pull requests across three production codebases were reviewed for design and code defects.*
The actual section body starts here…
Drop cap
The first paragraph of the very first section gets a drop cap automatically. No syntax needed — write the paragraph normally.
Inline formatting
**bold**, *italic*, `inline code`, [link text](https://url),
==highlighted text==, footnote reference[^1]
Lists
Standard Markdown ordered and unordered lists. Use the form **Anti-reference lists in the prompt.** Body of the item. to give numbered items a bold lead-in:
1. **Anti-reference lists in the prompt.** Five minutes of work, reduces defects.
2. **Reference-driven prompts.** Naming two or three specific products…
Tables
Pipe syntax with header + separator + rows. Use : in the separator for alignment (right-align is the most common case):
| Failure mode | Surface | Frequency |
|---|---|---:|
| Card-grid reflex | Marketing | 38% |
| Hallucinated APIs | Hooks | 9% |
Code blocks
Fenced with optional language and filename. Filename appears top-right of the block:
```ts src/agent/runner.ts
import { Agent } from './agent';
export const run = async () => { ... };
```
The renderer applies a calm 3-color syntax tint (keywords in terracotta, strings in muted teal, comments in italic gray). It is intentionally simple — agents should not try to highlight code by hand.
Callouts
Two systems coexist. Use the quiet typographic variants for soft mentions and emphasis; use the boxed variants when the content genuinely warrants a visual interruption.
Quiet (typographic, no box):
> [NOTE] A soft mention or aside.
> [INSIGHT] A non-obvious finding worth foregrounding.
> [CAUTION] A soft warning.
> [ASIDE] A tangent that doesn't belong in the main flow.
Boxed (subtle tinted panel + icon):
> [INFO] An important fact the reader needs to know.
> [WARNING] A must-know caution. The reader could otherwise be misled.
> [TIP] An actionable suggestion or pro-tip.
> [DANGER] Do not do this. High-severity warning.
Pick the lightest variant that does the job. Default to the quiet variants. Reach for boxed only when the content really deserves the visual weight.
Footnotes
The defect rate was lower than expected.[^1]
[^1]: Specifically, one defect per ~300 lines of changed code.
Footnote references render as superscript and show a hover popover on desktop. All footnote definitions are collected and rendered in a Footnotes section at the end. The numbers can be any string ([^1], [^method]); the renderer slugs them for IDs.
Definition lists
Term
: definition body, possibly long enough to wrap.
Another term
: another definition.
Useful for compact reference blocks at the end of a section ("Lowest cost / Highest leverage / Doesn't work").
Images and figures
A bare image on its own line becomes a framed figure with click-to-zoom:

The renderer wraps it in <figure class="framed"> with a centered caption underneath. Clicking the image opens it in a fullscreen lightbox.
For inline SVGs (charts, diagrams the agent constructs by hand), the renderer accepts  as a placeholder; the agent should then post-process the rendered HTML to inject the SVG. Most agents will not need this — prefer real image URLs.
Horizontal rule
Three dashes on their own line: ---. Use sparingly.
Output convention
- Path:
reports/<title-slug>-<YYYY-MM-DD>.html (the renderer derives this from frontmatter; do not pass the output path explicitly unless the user asks for a specific filename).
- Self-contained: the HTML file references Google Fonts via CDN but otherwise has no external dependencies. The user can open it directly with
open <path> (macOS) or by double-clicking.
- The original Markdown is embedded in a
<script id="source-md" type="text/markdown" data-source-path="<basename>.md"> tag inside the HTML. Clicking "Save as MD" in the rendered page exports the exact source the agent wrote. Lossless round-trip. The data-source-path attribute lets the review server mirror comment writes back to the original .md file so the source stays canonical.
- Review comments survive re-renders. When the review server writes a new (or edited / resolved / deleted)
@report-comment marker, it updates both the HTML's embedded source-md AND the sibling .md file. So node render.mjs <report.md> later picks up the same comments rather than discarding them. An agent reading the .md sees every comment with its target block and status.
- Every interactive render auto-starts (or reuses) one review server per
reports/ directory and opens the rendered report in the default browser. The server roots at the directory, so sibling reports, index.html, and index.json all resolve through the same port — Cmd-O navigation just works. A /tmp sidecar pid file keyed by the directory tracks the running server so subsequent renders to the same dir reuse the same port instead of stacking new processes. Pass --no-open to skip (batch re-renders); auto-open self-disables under non-TTY stdio (tests, CI).
Reports index
Every render also regenerates reports/index.json (machine-readable) and reports/index.html (the editorial landing page — a sortable table with date, title, tags, reading time, and status). Open reports/index.html directly to browse the vault.
Inside any rendered report, press ⌘O (mac) or ⌃O (other) to open the Cmd-O switcher: a centered command palette that fuzzy-filters across title, tags, summary, and eyebrow. ↑/↓ to navigate, ↵ to open the active result, ⌘↵ to open in a new tab, 1–8 to jump directly to that row, Esc to close. The same action is available from the Reports button in the topbar.
The Cmd-O list is kept fresh by a sweep at the end of every render: each report's embedded <script id="report-index" type="application/json"> block is rewritten to mirror the current index.json. No runtime fetch is required, so the switcher works under file://.
Recommended structure
A good report has these top-level sections, in this order:
- An optional eyebrow (small caps line above the title) — set via
eyebrow: in frontmatter.
- Title (
title: in frontmatter).
- Summary / lede (
summary: in frontmatter) — one or two italic sentences setting up the report.
- Metadata block — auto-generated.
- Body sections (
##) — each with a section lede in italic.
- A "What works" or "Recommendations" section when the report is investigative.
- An "Open questions" section when the report leaves things unresolved.
- Footnotes — auto-collected from
[^1] references.
Most reports do not need a Sources or Glossary section. Use them only when the report genuinely cites external material or defines jargon worth a glossary.
Anti-patterns
Things that are out of scope for this skill — do not try to make the renderer support them:
- Inline HTML inside Markdown body (the renderer escapes it).
- Custom CSS or
<style> overrides per report.
- Embedded JavaScript or interactive widgets.
- Multi-document reports (each report is a single HTML file).
- Reports without a frontmatter block (the renderer will fail with an error).
If you find yourself wanting any of these, the report should probably be a different kind of artifact (a real web page, a notebook, a slide deck) — not this template.
Worked example
A complete minimal report:
---
title: Cache invalidation strategies in the ingest pipeline
summary: Three strategies tried, one that worked, and a recommendation.
generated_by: Claude Opus 4.7 (1M context)
date: 2026-05-05
tags: [caching, ingest, performance]
sources: 6
version: 1
---
## The problem
*The pipeline was returning stale data after upstream schema changes. We tried three approaches before finding one that held under load.*
The cache layer in front of the ingest pipeline was returning rows from the previous schema version even after a successful migration. Investigating revealed three plausible failure modes and one actual one.[^1]
## What worked
*A versioned cache key plus a two-minute soft-expiry window absorbed the migration without a stampede.*
1. **Versioned keys.** Every cache entry includes the schema version in its key. Old keys are simply orphaned at migration time and evicted on TTL.
2. **Soft expiry.** Entries return cached values for up to two minutes after their TTL while a background refresh runs.
> [TIP] If your cache layer supports `stale-while-revalidate`, lean on it instead of building soft expiry by hand.
## What didn't
> [WARNING] Do not try to invalidate by pattern across a large keyspace. We attempted this with `KEYS schema:v1:*` and stalled the Redis instance for 40 seconds.
## Open questions
- Should the version be in the key or in a header? The current design uses the key; a header would let us version-roll without orphaning.
- Is two minutes the right soft-expiry window? It was picked from a hat.
[^1]: The actual cause was a race between the schema migration and the cache warmer, not a logical bug in the invalidation code.
Render it:
node ~/.claude/skills/report/render.mjs reports/cache-invalidation.md
The output lands at reports/cache-invalidation-strategies-in-the-ingest-pipeline-2026-05-05.html. Tell the user the path; they open it.
Files in this skill
SKILL.md — this file.
render.mjs — Markdown → HTML renderer. Pure Node, zero dependencies. Usage: node render.mjs <input.md> [output.html].
extract.mjs — pull the embedded source Markdown out of a rendered HTML report (used when the .md has been lost). Usage: node extract.mjs <input.html> [output.md] [--force].
template.html — the document shell (head, scripts, layout). The renderer fills its placeholders.
example.html — a fully-rendered demo report ("On the failure modes of LLM-generated frontend code") for visual reference. Open it in a browser to see every supported component in action.