feat: compile-check replays a changed-files check whose inputs did not change
Merged
HA
1d ago+326+326+012345678901234567890123456789-29-29-01234567890123456789
Summary
- Running compile-check again on the same edit no longer checks the same changed files a second time. When an assembly's inputs, its baseline and the worker build are all the ones the previous changed-files check saw, the run reports that check's result without starting the worker.
- The output says so:
FastChecks[].Reusedistruefor a check that was replayed.
User Impact
- Before: every run re-checked each fast-checked assembly through the worker, even when nothing had changed since the last run. That happens often when an agent fixes another assembly or simply runs the command again to confirm. Each repeat cost about as much as the first check.
- After: a repeat of the same edit is answered from the record of the last check. The
--fullreminder andUncheckedCodesstill show, because the replayed assembly stays among the fast-checked ones. - On a large project (561 assemblies, one method body edited in a large assembly), the second run over the same edit took 0.48 s against 1.93 s for the run that checked it (medians of three edits), a quarter of the time. Details under Verification.
Changes
- When a check is replayed. Three keys must match the record: the input key, the baseline's input key and the worker build key. The record is looked up inside the check, after every condition the check itself needs has passed: the baseline is usable, the non-source inputs and generator output are unchanged, 1 to 30 files changed, and the worker build matches the baseline. A check is therefore replayed only where it would have run, for the inputs it ran on.
--fulland--allnever reach it. - What is recorded. A fresh check that the run uses is recorded through the record store added in the previous PR. Recorded: success, the merged diagnostics, the changed files, the unchecked codes, and whether generators were not rerun.
- Edits during a check. Before recording, the input key is computed again. The worker reads the sources while it checks, so an edit made during the check would otherwise be recorded under the key of the bytes before it. Putting those bytes back would then replay what the edited file reported. When the key moved, the result still answers this run, but nothing is recorded, and the next run checks again. An edit that is made and then undone within one check still cannot be told apart; seeing it would need the worker to report hashes of what it read.
- A failed write fails the run, as a compile result that cannot be recorded already does. This record exists for the next run, like the compile result.
- Output.
UnitResult.FastCheckReusedis new and maps toFastChecks[].Reused. The replay deliberately does not setUnitResult.Reused. That flag moves an assembly toReusedAssemblies, which would drop it fromFastChecksand hide the--fullreminder andUncheckedCodes. - Changed test expectation.
TestFastPathCleanResultKeepsBaselineFilesused to require that a clean check leave every file the check keeps for the assembly untouched. A check now adds its own record. The test therefore requires every earlier file to keep its hash and exactly one file to be added. It still does not name the record, so the package stays unaware of the store's file names. - The compile-check doc, the skill and ADR 0015 describe the record, the replay and
Reused.
Verification
-
New tests:
- a second run over the same edit replays: no worker call, every reported field the same, with an error so a wrong
Succeededshows; - another edit checks again and replaces the record;
- a changed worker build falls back instead of replaying;
- missing generator output falls back instead of replaying;
- a
--fullcompile leaves no record of the earlier check; - a record that cannot be written fails the run;
- an edit during the check leaves no record, so reverting it checks again;
- the dispatcher marks a replayed check and keeps it fast-checked, with the
--fullreminder.
- a second run over the same edit replays: no worker call, every reported field the same, with an error so a wrong
-
Mutations, each restored afterwards:
Mutation Fails Look up before the worker check, with the baseline's worker key the worker-build test Look up right after the baseline loads the worker-build and generator-output tests Ignore a failed write the failed-write test Set Reusedinstead ofFastCheckReusedthe replay test and the another-edit test Never set FastCheckReusedthe replay test and the another-edit test Never record the replay, another-edit, failed-write and clean-check tests Record without UncheckedCodesthe replay test Record without GeneratorsNotRerunthe replay test Replay without the changed files the replay test Forgetkeeps the recordthe --fulltest and the store's testSkip recomputing the key before recording the edited-during-check test Dispatcher leaves Reusedunsetthe dispatcher test -
cli/dispatcher:golangci-lint fmt --diffis empty;go vet ./...passes;golangci-lint runand the complexity config both report 0 issues.go test ./...andgo test -race ./internal/compilecheck/...pass.- Coverage with the CI exclusions: 94.24% (baseline 94.2).
-
The compile-check package source has no name of the record (
git grep -Pfor the record type, its constants and its file name returns nothing). -
check-skill-sizepasses. The skill is 6,818 bytes.scripts/sync-tool-docs.sh --checkreports no drift. -
cli/release-automation:go test ./...passes. The file-length check passes. -
Measured on a large Unity project (561 assemblies), with one method body edited in a large assembly. This build and the previous integration-branch build ran alternately on the same output directory, over three different edits:
Run Median real time Previous build, checking (6 runs) 1.79 s This build, first run on an edit (checks and records) 1.93 s This build, second run on the same edit (replays) 0.48 s - Recomputing the key before recording does not show above the noise: against the previous build's run just before it on the same edit, the first run differed by -270, -70 and +40 ms.
- The JSON output of every replay matched the run that checked the edit, apart from
FastChecks[].Reused; so did a replay of an edit with a type error. The first run's output matched the previous build's. - The binary was built from the head of the stacked follow-up (#3330), which changes only how a full compile records its result; the changed-files check is the code in this PR.