/explain-diff-html skill — contract¶
Project-local Claude Code skill at .claude/skills/explain-diff-html/. Invoked as /explain-diff-html [branch | PR number | commit range | file paths].
Three files:
| File | Role |
|---|---|
SKILL.md |
The instructions Claude loads |
validate_explanation.py |
Hard gate — stdlib-only checker run against the generated page |
reference.html |
A minimal page that passes the gate cleanly; the executable version of the spec |
Why this exists¶
Explaining a change to someone who did not write it is a recurring cost on this repo — onboarding a collaborator onto the discovery engine, handing a landscape-loop run to a reviewer, writing up why a backend swap changed the numbers. The default artifact for that is a long chat message, which is unskimmable, unlinkable, and gone by the next session.
An HTML page fixes the durability problem and introduces two new failure modes that this skill exists to prevent.
Narrating the diff instead of explaining the change. A page that walks files top to bottom leaves the reader knowing which lines moved and not what the system now does. The skill front-loads investigation — callers, tests, data models, prior behavior — and treats the diff as evidence for the explanation rather than its outline.
Shipping a quietly broken artifact. These pages fail in ways invisible to the author: a CDN font that never loads on the reader's machine, a <pre> missing white-space that collapses every code sample to one line, a quiz whose correct answer is always the longest option. Rereading your own HTML does not catch these. A validator does.
The contract¶
A page is deliverable only if validate_explanation.py exits 0. The checks:
| Check | Fails when | Why it matters |
|---|---|---|
document |
No doctype, no closing </html>, empty <title> |
Truncated writes are silent |
offline |
Remote src/href asset, @import, url(https://…), fetch, XMLHttpRequest, WebSocket |
The page must render with the network off. Outbound <a href> links are explicitly allowed — references, not dependencies |
code-blocks |
No CSS rule targeting pre sets white-space: pre/pre-wrap |
Every code sample collapses to one line, and the page still looks fine to whoever wrote it |
diagrams |
Box-drawing characters or ASCII art inside <pre> |
Diagrams must be semantic HTML + CSS |
structure |
A required section missing, or a table-of-contents link pointing at a nonexistent id | Broken navigation on a long page |
quiz |
Not exactly 5 questions, malformed entries, empty explanation, "all/none of the above" | Shape the rest of the quiz checks depend on |
quiz-position |
Correct answer in one slot 3+ times, or spread over fewer than 3 distinct slots | Position becomes the answer key |
quiz-length |
Correct option is the single longest in more than half the questions | The classic tell — scoreable without reading the page |
quiz-leak |
data-correct, a correct class, correctness in aria-label, or only the answer present in static markup |
Answer visible before selection |
a11y and document also emit warnings (missing :focus styling, no viewport meta, inline onclick) that do not block but should normally be fixed.
The quiz-data contract¶
The validator can only audit the quiz because the questions live in one machine-readable block:
<script type="application/json" id="quiz-data">
[{"question": "...", "options": ["...", "..."], "answer": 2,
"explanation": "...", "distractors": {"0": "the misreading this encodes"}}]
</script>
Two consequences worth stating explicitly:
answerindexes the options as emitted, and the order is final. Shuffling happens at authoring time. A runtimeMath.random()shuffle would make the position-balance rule unverifiable and the page non-reproducible.- Option elements carry no correctness marker. The script reads
answerfrom the JSON on click. This is what makes "do not leak the answer through markup" mechanically checkable rather than a matter of trust.
Scope¶
- In scope: any code change — this repo's or another's. Nothing in the skill is OGUR-specific.
- Out of scope: bug review (
/review-engine,/code-review), API reference docs, and anything whose deliverable belongs in version control. The output is a throwaway teaching artifact written to/tmp/YYYY-MM-DD-explanation-<slug>.html, deliberately outside the repo.
How it differs from neighbouring surfaces¶
| Surface | Output | Best for |
|---|---|---|
git show / gh pr diff |
The raw change | You already know the system |
/code-review, /review-engine |
Findings about correctness | Deciding whether to merge |
Notion:tasks:explain-diff |
A Notion doc | Explanations that belong in the team wiki, editable and commentable |
/explain-diff-html (this skill) |
One offline HTML page with diagrams and a quiz | Teaching the change to someone who did not write it, including your future self |
The Notion sibling is the closer comparison. Reach for it when the explanation should live in the workspace alongside other docs; reach for this one when you want a self-contained file that works offline, renders diagrams, and tests comprehension.
When to invoke¶
- Onboarding someone onto a subsystem they have not touched
- After a change whose reasoning is the valuable part and would otherwise be lost — a backend swap, a gate insertion, a scoring-rule change
- Before a design review, so attendees arrive with the mental model already loaded
- On your own branch when it has been open long enough that you will not remember why
Rule of thumb¶
The reader should be able to predict what the system does in a case the page never showed them. If the page only supports recall of what it stated, it explained the diff and not the change. The quiz is the cheapest test of this: if every question is answerable by scanning back for a matching phrase, the explanation has not done its job.