Files
Paul HittandClaude Opus 5 e5e735f9ff Run the parity scrub in CI instead of weekly
Its own documentation says a check that only runs when remembered stops
running, and this one had: it spent long enough reporting four HIGHs
that were all wrong that reading it was a waste of a minute, which is
exactly the state SCRUB.md warns about two paragraphs further down.

All three repositories are public, so the job clones the two siblings
with no credentials, the same way vectors-head already takes
flamenet-e2e. Shallow, because the scrub reads source and not history.

Verified by running the job's exact command against fresh clones of
published main: 0 findings, exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 16:15:04 -04:00

2.5 KiB

The scrub

Runs in CI on every merge request and every push to main (parity-scrub), which clones the two sibling repositories itself. All three are public, so it needs no credentials.

By hand, with the three checkouts side by side:

python3 Scripts/parity-scrub.py

It exits non-zero on a HIGH finding.

It was weekly-and-by-hand until 2026-09-20, and by then it had been reporting four HIGHs that were all wrong for long enough that nobody was reading it. That is the state the warning further down is about, and it is why this is in the pipeline now rather than in somebody's calendar.

Why these checks and not others

Every check here has a SILENT failure mode. A feature missing from one app is visible the moment somebody opens the screen. These are not:

  • Envelope kinds. A kind one app sends and the other cannot parse is swallowed as unknown control and shows nothing at either end. The sender is not told. Bridge sharing had exactly this shape for a few hours on Between the Android half landing and the iOS half.
  • Unwired capability. A method on the view model that no screen calls. shareBridge shipped that way the same day: you could receive a bridge and had no way to send one.

A scrub that cries wolf is worse than no scrub. The first version of the unwired check reported 26 findings, of which almost all were wired perfectly well. Kotlin passes these as method references (run = model::hangUp), which the pattern missed. Anything this reports should be worth reading. If it starts producing noise, fix the check rather than learning to skim it.

What it cannot tell you

It reads source, so it knows what is declared, not what works. It cannot see that a button is behind a screen nobody can reach, that a toggle is stored and ignored, or that a route returns the wrong thing. Those need the app.

It also does not compare features, the settings each app offers, the screens each has. That part is manual, and that run found eight preferences on iOS with no Android counterpart, which no automated check here would have raised.

The manual half, in order

  1. Settings offered by each app, side by side. iOS: Sources/Views/Settings/. Android: the SettingsSection { … } list in ChatScreens.kt.
  2. For anything present on one side only, is it missing, or is it wired to nothing on the side that has it? Both are findings; they need opposite fixes.
  3. Relay: is main green, and is the deployed image built from it? A relay running a month-old build is the quietest kind of drift.