* test: frozen replay-compat corpus with expected verdicts (#1417) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: pin replay-compat corpus bytes to released blobs and assert via parseReplayInput Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: lock replay-compat provenance kind by corpus area and verify it in CI Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * docs: describe corpus provenance-kind lock and CI job Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: prune replay-compat corpus to minimal witnesses per shipped form Reviewer feedback on #1436: the mechanism earns its place, the dataset did not. Drop the 30 corpus entries whose bytes repeat a syntactic form or a migration refusal another entry already witnesses (platform twins and adjacent-release re-recordings), leaving 22 deliberate entries; make note required and state per entry which form or refusal it is the sole witness of. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: address corpus review nits (typed coverage list, cap rationale, derived-citation note) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * docs: split corpus rule — form from the release, verdict from today's parser Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * docs: format corpus README emphasis markers Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Michał Pierzchała <thymikee@gmail.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
5.9 KiB
Replay-compat corpus
.ad scripts as released versions of agent-device wrote them, each paired with the verdict
today's parser owes it: parses, or fails with an error code and the migration hint substring.
corpus.test.ts runs in unit-core and asserts every verdict through parseReplayInput — the same
composition replay/test use — so a grammar change cannot quietly alter which historical script
surface still loads. #1417.
The freeze rule
Never edit a script under scripts/ to make a parser change pass.
A corpus script is a recording that already exists in the wild; editing it deletes the evidence the gate is here to keep, and the pinned identities below make an edited script fail rather than pass. When a grammar change flips a verdict:
- Leave the script byte-identical.
- Update its
verdictinmanifest.tsin the same PR. - Say in the PR description which historical surface broke, and why the new refusal is the intended migration rather than a regression.
A verdict that changes from parses to fails is a compatibility break by definition. That is
allowed — the corpus only insists it be deliberate, reviewed, and paired with a migration hint good
enough to put in front of whoever recorded the script.
What is in the corpus
scripts/integration/andscripts/examples/— mined from the git history oftest/integration/replaysandexamples/test-app/replays.scripts/docs/— surfaces the released grammar wrote that those two suites never exercised (${VAR}parameterization, reserved/orderingenvrefusals,wait stableand landmark waits,target-v1annotations, retiredgesture rotatevelocity andgesture swipedurationMs).
What earns an entry
This is a parser-compatibility corpus, not a device matrix. An entry has to be the only witness of something:
- a shipped syntactic form no other entry's bytes contain, or
- a distinct migration refusal (error code + hint) no other entry already provokes.
So the same flow recorded on another platform, or re-recorded at an adjacent release with only
coordinates and labels changed, does not get an entry: it costs maintenance and adds no parser
leverage. Each entry's note says which form or refusal it is the sole witness of; if a new entry
cannot claim one, it does not belong here. Keep the corpus in the tens of entries, not a mirror of
the replay-fixture tree.
How "frozen" and "released" are enforced, not asserted
Provenance lives in manifest.ts. recordedBy is the released tag whose grammar produced the
surface, and provenance pins the bytes:
minedentries carry the git object id of the historical blob.corpus.test.tshashes the checked-in bytes and must reproduce that id, so a rewritten script cannot be made green by editing the manifest to match — the id is only obtainable from the released content.derivedentries (scripts/docs/) have no historical blob to point at, so their bytes are pinned by SHA-256. Only the bytes are machine-checked: thefromcitation naming the released grammar or doc that emitted the surface is reviewer-verified, so a derived entry has to be reviewed against its cited source at that tag (git show <tag>:<path>) before it lands.
Two sources, never invention. The form comes from the cited release — the script bytes are what
that tag's grammar or docs emitted. The verdict comes from today's parser, because a migration
refusal for a retired form did not exist at the tag that emitted it (v0.16.8 wrote
gesture rotate <deg> <x> <y> <velocity>; the hint telling you to drop the velocity is current
output). So copy the form from the release and paste the code/hint from an observed run — never
paraphrase either.
The kind is not a free choice: provenance-rules.ts fixes it by corpus area (scripts/integration/
and scripts/examples/ must stay mined, scripts/docs/ is derived), so an edited script cannot
be relabelled derived, re-pinned by digest, and thereby skipped by the history check. Both the unit
test and the verifier enforce that rule, and a new area must declare its kind before entries can live
under it.
pnpm check:replay-compat closes the other half: it re-derives every mined id from git history
(git rev-parse <recordedBy>:<path>) and checks every cited tag against git tag --list, so an
entry cannot claim a version that was never cut. It needs full history and tags, so it runs in its
own Replay-Compat Provenance CI job (fetch-depth: 0) rather than inside the shallow-clone-safe
unit lane, and any change under test/replay-compat/ selects it in pnpm check:affected.
Released surfaces only. A grammar state that only ever existed between commits is not compat
surface — git tag --contains <commit> decides, and unreleased shapes stay out (AGENTS.md,
"Unreleased API surface dies free"). The corpus is baselined against tags up to v0.20.0.
Adding an entry
- Find the surface at a release tag (
git show <tag>:<path>, or the released grammar/docs that emitted it) and copy it verbatim intoscripts/<area>/<name>.<tag>.ad. - Add a
manifest.tsentry withrecordedBy,provenance,covers, and anotenaming the form or refusal it is the sole witness of. For a mined entry the blob id isgit rev-parse <tag>:<path>; for a derived one it isshasum -a 256 <file>. Add the tag toREPLAY_COMPAT_RELEASED_TAGSif it is new. - Take the
verdictfrom what the current parser does with those bytes: start fromverdict: { kind: 'parses' }, run the suite, and if it refuses, paste the reported code and hint substring verbatim. Do not transcribe a hint from a release, an issue, or memory — the refusal is produced by today's parser, not by the tag that emitted the form. - Run
pnpm exec vitest run --project unit-core test/replay-compatandpnpm check:replay-compat.
Add an entry when a change retires, renames, or narrows a .ad form — the corpus is the record of
what that costs someone with a saved recording.