Skip to content

fix(ecad): the SI receipt was measuring copper that is not a route#693

Merged
ecto merged 5 commits into
mainfrom
si/full-board-holds
Jul 25, 2026
Merged

fix(ecad): the SI receipt was measuring copper that is not a route#693
ecto merged 5 commits into
mainfrom
si/full-board-holds

Conversation

@ecto

@ecto ecto commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Re-measures the full-board CM5 vcad.si-claims/1 receipt after #684 and fixes
three fail-closed gaps it exposed. It does not reach all-claims-HOLDS, and
the reason is the point of the PR.

What the re-measurement found

The premise going in was that two claims needed closing. Measured on a fresh
full route, three of four were broken — vias_per_si_net had regressed through
its bound — and the headline skew turned out not to be skew at all:

/USB3-0.TX_P was being scored as a routed differential pair with 0.23mm of
copper across an 18.36mm pad span
, while the DRC was already reporting it
UnconnectedNet. Comparing every leg's copper against its own straight
pad-to-pad distance — a ratio needing no connectivity model, so it cannot be an
artifact of how one defines an island:

board legs copper < 50% of span min ratio
human 86 0 1.03
ours 84 7 0.01

The reported 38mm "skew" was essentially the length of whichever leg did
route. Compensation was never going to close it — there is no second leg to
match.

Three fixes

1. The pruner was defeated by shorts. dangling_copper_mask is documented
as dropping copper whose island touches no pad "of their net", but it judged
anchoring over the board-global touch graph and accepted any pad in the
component — so a dead island of net X that merely brushed net Y saw Y's pads and
survived. On a board with 845 shorts that is not a corner case. Now judged over
same-net connectivity, reusing the contact list the global pass already builds
(no extra broadphase); net ties still connect.

2. The receipt measured legs that are not routes. si_claims gated
"measured" on net_routed_length > 0, which sums any copper carrying the net
name. And coupled_fraction normalizes by P alone, so a stub beside a full twin
scores 1.000 — /MIPI1.D3_P read coupled 1.000 while carrying 38.52mm of
skew. The metric rewarded the failure. A pair is now excluded when either leg
holds less copper than its own pad span requires, and the exclusions are counted
and bounded by a new si_pairs_incomplete claim, because excluding silently
would be worse than the original bug: a board could pass by leaving the hard
pairs as stubs.

DRC continuity was tried as the gate first and is disqualifying — the
fixture is reverse-engineered, and continuity calls 25 of the human board's 49
pairs discontinuous, which would break the calibration anchor. The threshold
(0.5) is calibrated to the anchor's own thinnest leg, 0.522.

3. si_finish handed back a dirtier board than it was given. route_all
prunes as its last word, but si_finish runs after it and every stage rips and
re-routes copper: 231 dead traces and 52 dead vias left stranded and counted as
routed length. This one was invisible without the end-to-end run — the fix in
(1) was working inside route_all (2030 dead traces pruned vs 393 when
re-pruning the saved board) yet the final receipt came out byte-identical,
because everything cleaned up was replaced afterwards.

Results — fresh full route, 874.5s, routability 0.994

claim before after bound
worst_group_skew 9.297mm 3.579mm ≤ 10.0 HOLDS
worst_intra_pair_skew 37.853mm 38.521mm ≤ 1.1 BROKEN
si_pairs_incomplete (new) 9 ≤ 0 BROKEN
min_pair_coupled_fraction 0.000 0.221 ≥ 0.5 BROKEN
vias_per_si_net 2.766 3.106 → 2.626 w/ spur pass ≤ 3.0 BROKEN

Route-attributable Clearance +150 → +135, NetIslands 93 → 17.
MinDrill/AnnularRing/MinTraceWidth fire on nearly every via and thin trace
on the human board too, so they are imported-rule artifacts and Clearance is
the rule that attributes.

What is left

worst_intra_pair_skew is unchanged at 38.521mm. /MIPI1.D3_N holds 77.03mm of
copper for a 34.54mm span, continuous with 2/2 pads and all of it on
pad-to-pad paths — a redundant cycle, the signature of a re-route that never
removed the path it replaced. Leaf-pruning cannot touch it (every node has
degree ≥ 2), and compensation would add copper to a net that already has twice
what it needs.

Also staged but deliberately not wired in: spur_copper_mask /
prune_spur_copper, which strips dead-end branches off live islands
(/USB3-1.DP carried 72.86mm whose shortest pad-to-pad path is 36.60mm — a
~34mm unterminated stub on a USB3 pair). It is sound — it changes neither
board's UnconnectedNet count — and it recovers vias_per_si_net to 2.626, but
for a partially-reached net every piece of copper is a dead end, so enabling it
reclassifies those nets as unrouted and moves routability. That contract
change needs its own full-board before/after.

Guarantees

  • No bound was changed. The human anchor re-verifies ALL HOLD after
    every commit and after the merge, with every pre-existing number identical
    (9.756 / 1.074 / 0.857 / 2.265).
  • Stripped-fixture DRC baseline unchanged at 311 short/clearance, 23
    Clearance — the connectivity fixes do not move it.
  • 299 tests pass, clippy -D warnings clean, cargo fmt clean. The new
    behaviour is pinned by dead_copper_shorted_to_a_live_net_is_still_pruned,
    verified to fail without the fix.
  • No packages/kernel-wasm artifacts touched.

Full measurement record, including what did not work (descend_board tunes 0 of
41 pairs on a full board; phase compensation reaches 3 of 41; construction
couples 47 of 49 but negotiation un-couples down to 10 below 0.5), is in
docs/gpu-router-m7-pair-si.md.

🤖 Generated with Claude Code

ecto and others added 5 commits July 25, 2026 12:33
…b copper

Re-measured the full CM5 SI receipt on e1a5d90 (post-#684) to check whether
the two broken claims had moved. They moved, and the run also invalidated the
reading of the skew claim, so this supersedes M7's "two blocker groups".

Three of four claims now break: vias_per_si_net regressed through its bound
(2.766 -> 3.128). worst_intra_pair_skew is 38.521mm, and it is not detour skew
— seven pair legs are not routed. Measured copper length against each net's own
straight pad-to-pad span, a ratio needing no connectivity model: human board 0
of 86 legs below 50%, ours 7 of 84, minimum 0.01 (/USB3-0.TX_P carries 0.23mm
for an 18.36mm span). /MIPI1.D1_P is four disjoint fragments, the largest a
floating 4.234mm stub, beside a complete 38.57mm twin.

Two gaps let that reach the receipt: si_claims gates "measured" on
net_routed_length > 0, and coupled_fraction normalizes by P alone, so a stub
beside a full twin scores 1.000 — the metric rewards the failure. The DRC misses
it too: each starved leg is bridged by copper it is SHORTED to, and the
connectivity walk crosses the short.

Also recorded, with the human-board control M7 lacked: MinDrill/AnnularRing/
MinTraceWidth fire on nearly every via and thin trace on the human board too,
so Clearance is the attributable rule — stripped 23 -> ours 173, i.e. +150.

What did not work: descend_board tunes 0 of 41 pairs on a full board (all
rejected by its fail-closed check) despite reaching ~0.006mm in isolation;
phase compensation fixes 3 pairs and leaves 38 over tolerance; and coupling
degrades after construction (47 coupled, 10 below 0.5 by the end) because
negotiation re-routes pair legs independently.

No bounds were touched. The human board re-verifies ALL HOLD, unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…osed on unrouted legs

Three findings from re-measuring the full-board CM5 SI receipt, each fixed or
staged with the measurement that motivates it. No bound was changed; the human
board re-verifies ALL HOLD with every original number identical.

1. The pruner was defeated by shorts. `dangling_copper_mask` is documented as
   dropping copper whose island touches no pad "of their net", but it judged
   anchoring over the board-global touch graph and accepted ANY pad in the
   component — so a dead island of net X that merely brushed net Y saw Y's pads
   and survived. On a board with 845 shorts that is not a corner case. Judging
   over same-net connectivity (build_same_net_dsu, built from the contact list
   the global pass already produces, so no extra broadphase; net ties still
   connect) removes 393 dead traces and 52 dead vias from the routed CM5 while
   UnconnectedNet holds at 253 — nothing lost a connection. worst_group_skew
   falls 8.397mm -> 3.579mm, Clearance 173 -> 150. UnconnectedNet gets the same
   graph, for the same reason: a short is not a substitute for a route.

2. The receipt measured legs that are not routes. si_claims gated "measured" on
   net_routed_length > 0, which sums any copper carrying the net name, so a pad
   escape qualified — /USB3-0.TX_P was scored as a routed pair with 0.23mm of
   copper across an 18.36mm pad span, while the DRC was already reporting it
   UnconnectedNet. coupled_fraction then normalizes by P alone, so a stub beside
   a full twin scores 1.000: /MIPI1.D3_P read coupled 1.000 at 38.52mm of skew.
   The metric rewarded the failure. Now a pair is excluded when either leg holds
   less copper than its own pad span requires, and the exclusions are counted and
   bounded by a new si_pairs_incomplete claim — excluding silently would be worse
   than the original bug, since a board could pass by leaving the hard pairs as
   stubs. DRC continuity was tried as the gate first and is disqualifying: the
   fixture is reverse-engineered, and continuity calls 25 of the HUMAN board's 49
   pairs discontinuous. The length test reads only copper length and pad
   positions. Threshold 0.5 is calibrated to the anchor's thinnest leg (0.522).

3. Spurs, staged not shipped. Dead-end branches hanging off live islands are
   invisible to an island-level pruner and large here: /USB3-1.DP carried 72.86mm
   whose shortest pad-to-pad path is 36.60mm, so 36.26mm was stub — a real
   unterminated stub on a USB3 pair, not just bad bookkeeping. spur_copper_mask
   implements iterated leaf removal and is sound (it changes neither board's
   UnconnectedNet count) and recovers vias_per_si_net 3.106 -> 2.626, but it is
   NOT wired into route_all: for a partially-reached net every piece of copper is
   a dead end, so it reclassifies those nets as unrouted and moves routability.
   That contract change needs its own full-board before/after.

Remaining: worst_intra_pair_skew is still 38.521mm. /MIPI1.D3_N holds 77.03mm
for a 34.54mm span, continuous with 2/2 pads and all of it on pad-to-pad paths —
a redundant cycle, the signature of a re-route that never removed the path it
replaced. Compensation is the wrong tool for it either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…es strand

route_all prunes dangling copper as its last word, but si_finish runs after it,
and polish_pairs / descend_board / meander_pair_skew all rip copper and
re-route it. So the board si_finish handed back was dirtier than the one it was
given: measured on the full CM5, 231 dead traces and 52 dead vias, which
net_routed_length counts as routed copper.

Caught by the end-to-end run and invisible without it. The same-net anchoring fix
was working inside route_all — the fresh route's prune dropped 2030 dead traces
where re-pruning the saved board found 393 — yet the final receipt came out
byte-identical to the unfixed baseline, because everything route_all cleaned up
was replaced afterwards.

Fresh full route, 874.5s, routability 0.994: worst_group_skew 9.297mm -> 3.579mm,
Clearance 173 -> 158 (route-attributable +150 -> +135), NetIslands 93 -> 17.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SiBounds gained fields on both sides (max_incomplete_pairs here, the impedance
pair from #691) — additive, both kept, and max_incomplete_pairs takes
#[serde(default)] to match the convention #691 established for new bounds.

Human anchor re-verified after the merge: ALL HOLD, six claims, every
pre-existing number unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chojiai

chojiai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Choji has used up today's reviews

Your team has reached the free plan's limit of 5 reviews per day, so Choji stepped aside on this pull request. That's a plan limit, not a reflection of the change itself.

Your free reviews reset tomorrow. To have Choji review every push without waiting, upgrade to Pro for unlimited reviews.

You're on the free plan · 5 reviews/day.

@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated (UTC)
mecheval Ignored Ignored Jul 25, 2026 5:28pm
vcad Ignored Ignored Jul 25, 2026 5:28pm
vcad-docs Ignored Ignored Jul 25, 2026 5:28pm

Request Review

@ecto
ecto merged commit 45afeb7 into main Jul 25, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant