Repository navigation
ci: prove the macos-arm64 baseline on a second machine, and keep it proved - #204
Conversation
…d machine tools/parity-audit/baselines/macos-arm64.sha256 is keyed on OS and architecture, which asserts that any osx-arm64 host reproduces those exact bytes. It had only ever been reproduced on the one Mac that recorded it (#188). A developer running --check-baseline on their own machine and getting eighteen red screens would most likely distrust the harness rather than read the caveat in the README, and a distrusted visual gate stops being run at all. Two changes on 2026-09-02 made the measurement cheap. #199 put the capture harness on macos-latest at all; #202 made the saved bytes a function of the visual tree rather than of the compositor's redraw history, which removed the ~1-in-5 six-pixel flip that would have made any baseline check on that runner a false-positive machine. So the macOS job now checks the committed baseline as well as determinism, and it does it over the SAME captures: --determinism proves the five runs agree with each other, then the new step verifies determinism/r1 against the manifest. That makes the verdict one statement about one set of bytes, and it costs no extra capture. run.sh gains --check-captured <dir>, which is --check-baseline's verification against a directory that has already been captured. Both modes now share one resolve_baseline preflight and one baseline_check, so the manifest lookup, the both-directions comparison and the failure text exist once. The failure-artifact steps now fire for either check, since a baseline failure wants exactly the same PNGs and they are already in determinism/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…fied The README's Baselines section said cross-machine reproducibility was plausible but untested and told readers to treat a mismatch on another machine as "compare the environments" first. Both halves are out of date: a GitHub macos-latest runner (macOS 26.5.2 / 25F84, image macos26) reproduces all eighteen hashes recorded on macOS 26.5 / 25F71, and CI now checks that on every pull request. Says what a red baseline step means, in order of likelihood, including the one explanation a developer's own Mac cannot reproduce — the runner image moving — and notes that the step prints the host on green runs precisely so a red one has something to be compared against. Documents --check-captured next to --check-baseline in the Run block. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
PR Summary by QodoGate macOS ARM64 baseline with deterministic CI captures
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo
1.
|
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 1b1d2a8 |
The new step's comment said that running the baseline check after --determinism means a red tick "can never" be a capture race, and the ::error:: text said the same in the place a maintainer actually reads it. The step above them documents the opposite: five runs miss a 50/50 per-run flip about 6% of the time. Told that a failure cannot be a flake, the natural response to an unreproducible one is to assume the message is wrong and re-run until green. Both now say likelier rather than certain, and name the case explicitly: a red that does not reproduce is a capture race that survived five samples — a fourth instance of the class #179 and #201 each turned out to be — and it wants a ticket rather than a retry. The README's list of what red means gains the same fourth entry. Found by Qodo on b849080..1b1d2a8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit d8b71af |
|
Qodo-clean, CI green — ready for review Sweep verification, independent of the author: head is still The new macOS step this PR adds — Committed baseline must be reproduced on this runner — ran and passed on Merge-order note — not a defect. This is the only one of #203 / #204 / #205 that changes the pipeline. The moment it lands, #203 and #205 are green on a pipeline that no longer exists: neither ever ran the new baseline step. I closed that gap locally rather than leaving it to a re-run — a #203+#205 test-merge onto |
Not merging — for review.
Closes #188.
What was wrong
tools/parity-audit/baselines/macos-arm64.sha256is the visual gate's reference: 18SHA-256 hashes, one per captured screen. Its name is keyed on OS and architecture, which
asserts that any
osx-arm64host reproduces those exact bytes. Nothing had ever checkedthat. It was recorded on one Mac, and #188's point is that the name was writing a cheque
the evidence could not cash.
The way that hurts someone is concrete. A developer runs
./run.sh --check-baselineontheir own machine, gets eighteen red screens, and has to decide whether they broke the UI
or whether their laptop simply renders differently. The README does warn them;
realistically they conclude the harness is untrustworthy and stop running it, which is the
exact failure #89 exists to prevent. It also left the CI question permanently
unanswerable: while the reference is a single-machine artifact wearing an OS+arch name, no
gate built on it can tell a regression from a host difference.
What changed
The measurement came back clean, so the work is to make CI keep it that way. A GitHub
macos-latestrunner reproduces all eighteen hashes byte for byte — a differentApple-silicon machine on a different macOS build (26.5.2 / 25F84, image
macos26) fromthe one that recorded them (26.5 / 25F71). The
macos-arm64name is earned.So the
Desktop head on macOSjob now checks the baseline as well as determinism, and thename stops being an assumption maintained by hope: the day two Apple-silicon hosts
disagree, a red tick says so on the pull request that did it.
It does that over the same captures, not a new one.
--determinismalready runs fivetimes and proves the five agree; the new step verifies
determinism/r1— one of the setsevery other run was just shown to byte-match — against the committed manifest. One
statement about one set of bytes, and it costs ~0.3 s, because capturing is the
expensive part and it had already happened.
The ordering is load-bearing rather than tidy. A baseline check on a host whose captures
race is a machine for manufacturing differences that are not regressions — which is why
this could not have been added before #202, when ~1 capture run in 5 on this runner
carried a six-pixel flip on the SplitView pane edge.
run.shgains--check-captured <dir>:--check-baseline's verification against adirectory that has already been captured. Both modes now share one
resolve_baselinepreflight and one
baseline_check, so the manifest lookup, the both-directions comparisonand the failure text exist once instead of twice. The failure-artifact steps fire for
either check now, since a baseline failure wants the same PNGs and they are already
sitting in
determinism/.Nothing was loosened. The determinism gate is untouched; this is strictly an
additional assertion on the same run.
What this asks of everyone else
A pull request that changes what any of the 18 screens looks like must now re-record
macos-arm64.sha256in the same commit, or the macOS job goes red. That was already thedocumented contract — this is the first time it is enforced.
When it does go red, the causes in order of likelihood are: a UI change that forgot the
re-record; a real visual regression; or the
macos-latestimage moving under us. Thethird is the one your own Mac cannot reproduce, so the step prints the runner's macOS
build and image version on every run, green ones included — a red run is diagnosed by
comparing it against a green one, and that needs both sides on the record.
There is a fourth, rarer case and the comments name it rather than pretending it away: a
red that does not reproduce is a capture race that survived five determinism samples
(which miss a 50/50 per-run flip ~6% of the time). That is a fourth instance of the class
#179 and #201 each turned out to be, and it wants its own ticket, not a re-run.
Verification
The claim this PR makes, on real runners. Four
Desktop head on macOSjobs, every onegreen on both steps:
acc8da318/18 across 5 runsbaseline OKb8490801b1d2a8d8b71af(The
1b1d2a8run shows as cancelled overall — the concurrency group superseded it whend8b71afwas pushed — but its macOS job had already completed green. Commits afteracc8da3change comments and README prose only; the workflow logic andrun.shareidentical across all four.)
From the first of those:
A correction to an earlier measurement while I was here. #191 reported 89 of 90
screen-by-screen comparisons between this Mac and the runner as byte-identical. That was
before #202. It is now 90 of 90:
determinism/r1matches the manifest, and runs r2–r5are byte-identical to r1, so all 18 screens agree across all five runs and with the
recording Mac.
Locally, on the recording Mac (macOS 26.5 / 25F71):
--check-baselineend-to-end after the refactor: all 18OK,baseline OK.--check-capturedon that same capture dir: identical verdict.appended) →
desktop-7…: FAILEDand non-zero; a missing screen →FAILED open or read;an extra screen the manifest does not list →
captured but absent from the baseline — the screen set has grown. Clean again once the extra is removed.no PNGs each fail with their own message rather than as eighteen missing screens.
if:expressions and thenew step id.
Build gate, under
build.2: Desktop build, iOS restore, iOS-c Release -r iossimulator-arm64 --no-restore— all green.git status --short -- '**/packages.lock.json'empty afterwards. The Android head is notbuildable on this Mac; CI covers it. No first-party C# changed, so no warning-count claim
is made either way.
Not verified: that a third Apple-silicon Mac agrees. Two hosts is two hosts, and the
README says exactly that rather than rounding it up to "portable".
Also, from the same investigation
#201 is closed on the CI record rather than on #202's title: 85 capture runs across 11
macos-latestjobs and 6 heads since the fix landed, zero flips, against a pre-fix rate of7 in 30. Full table on the issue.
Diff
git diff --numstat origin/main...HEAD—.github/workflows/build.yml91/19,tools/parity-audit/run.sh105/37,tools/parity-audit/README.md49/15. Well over halfof the first two is comment prose, and no C# is touched.
🤖 Generated with Claude Code