Found while trying to A/B the #678 pour-promise weld. It is not a bug in that feature; it is a property of the replay chain that invalidates single-run A/B on any board whose chain contains plane repair or an oracle step — which is most of the interesting ones.
The measurement
Two replays of orangecrab's recorded 15-command chain (runs_set3/orangecrab/redo_commands.sh), pruned to the file-dependency chain, same input board, same flags, same commit 2e15780d, same machine, minutes apart:
| arm |
commit |
real DRC |
connectivity issues |
routed nets |
oc_base2 |
2e15780d |
1 |
8 |
154 |
oc_fix2 |
2e15780d |
3 |
14 |
152 |
The final boards differ by ~1976 of 5872 segments. Nothing about the code differed between them — I had labelled the directories "base" and "fix" and only caught it by reading redo_stress_test.py's own first line, Code under test: ... (commit <sha>), which prints the version for exactly this reason.
Why it matters
I read that pair as "the change regresses the board", gated a feature on it, and wrote the number into a commit message, CLAUDE.md and a code comment before the check caught it. A pair like this is convincing: it has a table, a direction and a plausible mechanism. Any future A/B judged on one replay pair of an oracle chain is a coin flip with a plausible story attached.
Concretely, the corpus A/B that eventually decided the #678 knob moved one board by one net. That signal is an order of magnitude smaller than this noise floor, so on a single chain replay it would have been undetectable in either direction.
Cause, almost certainly known
This repo has already diagnosed and fixed one instance of this class: route_disconnected_planes's ±1-segment wobble was kicad-cli's own run-to-run nondeterminism — its threaded fill/ratsnest jitters the reported unconnected-item anchor coordinates between identical invocations, and oracle_reconnect's decisions keyed on the raw coordinates (same-position test, Zone|Zone split, retry-cap keys, seeds) flipped with them. Fixed in c62ca21 by _snap_zone_anchor canonicalising Zone anchors to the lex-min cell of the traced fill island, plus processing links in canonical sorted order. After that fix: 6 oracle runs across different-UUID boards identical, 3 full repair chains 0 geometric diffs.
The spread measured here is far larger than ±1 segment, so either that fix does not cover this path, or a new raw-coordinate consumer has appeared since — which is precisely the "if a new wobble appears, suspect a new raw-coordinate consumer" case that finding anticipated.
Suggested first steps
- Isolate the stage. Replay the chain with the oracle/plane-repair steps stubbed out and confirm the earlier steps are bit-reproducible (a single
route.py step demonstrably is: in a separate three-arm test the control and a no-op arm produced byte-identical copper — 6097 segments and 625 vias compared field by field).
- Rerun the oracle alone, twice, on the same file — the documented way to confirm kicad-cli report jitter — and diff the reported anchors, not just the outputs.
- Audit for new raw-coordinate consumers of kicad-cli-reported positions added since
c62ca21; canonicalise any to island/cluster identity rather than branching on raw equality.
Meanwhile
CLAUDE.md now records the rule: grade a plane/oracle chain change by a corpus A/B, never by one replay pair. That is a workaround, not a fix — corpus A/B costs ~$1 and 40 minutes, and it should not be the only way to answer a question about one board.
Refs #490 (repair under refill jitter), #659, #678.
Found while trying to A/B the #678 pour-promise weld. It is not a bug in that feature; it is a property of the replay chain that invalidates single-run A/B on any board whose chain contains plane repair or an oracle step — which is most of the interesting ones.
The measurement
Two replays of orangecrab's recorded 15-command chain (
runs_set3/orangecrab/redo_commands.sh), pruned to the file-dependency chain, same input board, same flags, same commit2e15780d, same machine, minutes apart:oc_base22e15780doc_fix22e15780dThe final boards differ by ~1976 of 5872 segments. Nothing about the code differed between them — I had labelled the directories "base" and "fix" and only caught it by reading
redo_stress_test.py's own first line,Code under test: ... (commit <sha>), which prints the version for exactly this reason.Why it matters
I read that pair as "the change regresses the board", gated a feature on it, and wrote the number into a commit message,
CLAUDE.mdand a code comment before the check caught it. A pair like this is convincing: it has a table, a direction and a plausible mechanism. Any future A/B judged on one replay pair of an oracle chain is a coin flip with a plausible story attached.Concretely, the corpus A/B that eventually decided the #678 knob moved one board by one net. That signal is an order of magnitude smaller than this noise floor, so on a single chain replay it would have been undetectable in either direction.
Cause, almost certainly known
This repo has already diagnosed and fixed one instance of this class:
route_disconnected_planes's ±1-segment wobble was kicad-cli's own run-to-run nondeterminism — its threaded fill/ratsnest jitters the reported unconnected-item anchor coordinates between identical invocations, andoracle_reconnect's decisions keyed on the raw coordinates (same-position test, Zone|Zone split, retry-cap keys, seeds) flipped with them. Fixed inc62ca21by_snap_zone_anchorcanonicalising Zone anchors to the lex-min cell of the traced fill island, plus processing links in canonical sorted order. After that fix: 6 oracle runs across different-UUID boards identical, 3 full repair chains 0 geometric diffs.The spread measured here is far larger than ±1 segment, so either that fix does not cover this path, or a new raw-coordinate consumer has appeared since — which is precisely the "if a new wobble appears, suspect a new raw-coordinate consumer" case that finding anticipated.
Suggested first steps
route.pystep demonstrably is: in a separate three-arm test the control and a no-op arm produced byte-identical copper — 6097 segments and 625 vias compared field by field).c62ca21; canonicalise any to island/cluster identity rather than branching on raw equality.Meanwhile
CLAUDE.mdnow records the rule: grade a plane/oracle chain change by a corpus A/B, never by one replay pair. That is a workaround, not a fix — corpus A/B costs ~$1 and 40 minutes, and it should not be the only way to answer a question about one board.Refs #490 (repair under refill jitter), #659, #678.