Semantic Diff

#8882 Draw commit records as semantic diffs of the compiler's objects

1 commit, drawn from its record.

Summary

Changelog draft

๐Ÿ  Internal
  • Draw commit records as semantic diffs of the compiler's objects, each kind of object in its own drawing, and extract the exact forms a record abstracts from the compiler with scripts/recordFacts.js. #8882

4e11b25c68710759cf66690b89598b83d2a33fad..pr-8882

6aed21f3a ยท Cristiano Calcagno ยท Oct 17, 2026 ยท #8882

Draw commit records as semantic diffs of the compiler's objects

๐Ÿ  Internalscriptsdocstests

Why

A record states its change as prose sections and a JavaScript Before/After pair. The objects most compiler changes touch, a typing rule, a parsetree node, a match compiler's clause matrix, a Lambda rewrite, an error message, have no form of their own in it: the reviewer reconstructs them from the code diff of typecore.ml or matching.ml.

addedCode scripts/recordRender.js, the drawing of a record

A pure module, with no file system and no git: it parses a record (sections, change blocks, git trailers) and draws it as HTML. Each kind of object has its drawing: a typing rule as an inference rule, an AST or typed tree fragment as a tree, a clause matrix as a matrix, Lambda and JavaScript as highlighted code with Before and After side by side, a runtime representation as ReScript โ†ฆ JavaScript lines, a diagnostic as its text. The kinds are a typed table, so a kind without a drawing does not type-check. A modified block's two versions of code, a tree or text are drawn as aligned rows: a line both have faces itself, the changed lines between are padded with gaps, and on a narrow screen the rows read as a unified diff. An added or removed block's one version spans the block. A long line wraps, its continuation indented past the line's own indentation or tree connectors. It checks each record against its diff: changelog fragments, the required trailers, the Tests trailer against the test files both ways (every listed path is in the diff; every test file in the diff other than a generated snapshot is listed, the missing ones named), generated JavaScript against Output, magic numbers, the v0 PPX bridge, super_errors fixtures and tests/ERROR_VARIANTS.md.

modifiedCode scripts/commitRecords.js, the Semantic Diff page

Reads each commit's record, files, magic-number diff and changes/ fragments from git and hands them to recordRender.js, which parses, checks and draws them. Its options are --out, --pr, --title and --fragment.

addedCode scripts/recordFacts.js, the facts a record abstracts

Prints, for one example and one compiler build, the parser's tree, the tree after the built-in PPX, the JSX transform's output, the formatter's output, the typed tree, the inferred interface, Lambda as translated and after each pass that changes it, the generated ES module and the diagnostic, each as the compiler prints it, under a line naming the compiler and the commit its checkout is at. Run with --bsc on a build of the parent commit, it gives a modified block's Before.

addedCode scripts/commitRecordHook.sh, a commit-msg hook, not installed

Refuses a message without the Area, Output, Tests and Signed-off-by trailers, with a trailer after Signed-off-by, or with a malformed Output or Tests; for a commit that changes compiler, runtime, build system or editor tooling sources, also one without Why: or a change block.

modifiedDocs .claude/skills/records/SKILL.md, the record format

The skill defines the kinds of object, each with the code that owns it, how to extract its exact form, its drawing and what a change of it brings; and the trailers, each with the check the card makes. A change that replaces one shape by another is one modified block on one example, Before extracted from a build of the parent commit and After from this commit's, the Source line naming both builds; added and removed blocks are for objects with no counterpart.

Before text
After text
<subject><subject><paragraph>Example (rescript), Before (js), After (js), Source, Breaking, ReviewArea, Output, Formatting, Formats, PPX, Editor, Errors, Tests,Measured, PR, Signed-off-by Why<Kind> <added|modified|removed> (<object>), with Before and Afterextracted from the parent commit's build and this commit'sExample, Effect, Source, Breaking, ReviewArea, Output, Tests, Measured, PR, Signed-off-by

Command

node scripts/recordFacts.js -e 'let f = (c, a, b) => c ? a : b' parse lambda jsnode scripts/commitRecords.js --out review.html --pr 1234 --title "Title" master..my-branchln -sf "$PWD/scripts/commitRecordHook.sh" "$(git rev-parse --git-path hooks)/commit-msg"
๐Ÿ  InternalDraw commit records as semantic diffs of the compiler's objects, each kind of object in its own drawing, and extract the exact forms a record abstracts from the compiler with scripts/recordFacts.js. #8882

Review

  • Do records in the earlier format still render? Yes: a top-level Before (js): and After (js): pair draws side by side, and trailers outside the format (Formats, Errors, ...) draw as facts.
  • Where do Formatting, Formats, PPX and Editor go? A formatter or editor change is a Format or Editor change block; the card reads the magic numbers, the v0 bridge fixtures and the error catalogue from the diff.
Output
unchanged
Tests
other~ scripts/commitRecords.test.js
  • Changelog fragment changes/record-object-kinds.internal.md
  • The tests listed are in the diff
  • Every test file in the diff is listed
  • No committed generated JS file modified
7 files, +2671 โˆ’1030
  • M .claude/skills/records/SKILL.md +308 โˆ’103
  • A changes/record-object-kinds.internal.md +1 โˆ’0
  • A scripts/commitRecordHook.sh +74 โˆ’0
  • M scripts/commitRecords.js +48 โˆ’914
  • M scripts/commitRecords.test.js +234 โˆ’13
  • A scripts/recordFacts.js +207 โˆ’0
  • A scripts/recordRender.js +1799 โˆ’0
with Claude Opus 5.5