Skip to content

fix: key skipped label collisions by label pair, not net pair - #721

Open
DPS0340 wants to merge 1 commit into
tscircuit:mainfrom
DPS0340:fix/label-overlaps
Open

fix: key skipped label collisions by label pair, not net pair#721
DPS0340 wants to merge 1 commit into
tscircuit:mainfrom
DPS0340:fix/label-overlaps

Conversation

@DPS0340

@DPS0340 DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown

Latent bug found while investigating the 7 remaining label-label overlaps from #720. It changes no board today — I'll be upfront about that — but it's a real aliasing defect with a direct test.

The bug

NetLabelNetLabelCollisionSolver gives up on a collision when it exhausts its candidate positions, recording the pair in skippedCollisionKeys so findNextCollidingPair won't retry it. The key was:

private collisionKey(a: NetLabelPlacement, b: NetLabelPlacement) {
  return [a.globalConnNetId, b.globalConnNetId].sort().join("::")
}

That identifies a pair of nets, not a pair of labels. Any two labels from netA and netB share one key, so abandoning the search for one pair silently suppresses every other pair on those same nets — including ones that were never examined and might well have been resolvable.

Scale of the aliasing across the corpus:

bug-report-20260706T220324Z:  459 net-pairs have >1 label pair (max 136)
bug-report-20260707T092615Z:  163 net-pairs have >1 label pair (max  70)
bug-report-20260707T140410Z:   73 net-pairs have >1 label pair (max  21)
bug-report-20260716T144856Z:   49 net-pairs have >1 label pair (max  30)

So one skip could mask up to 135 unexamined pairs.

The fix

Include pinIds in the key. It identifies the individual label and is stable across relocations of that label, so distinct pairs stay distinct while a label being moved keeps its identity.

Honest scope: no board changes

I measured before and after across every imported bug report:

before:  insideChips=8  overlappingPairs=7
after:   insideChips=8  overlappingPairs=7

Identical. I checked why, rather than shipping and claiming an improvement: among label pairs that actually overlap today, no two share a net-pair key. The 5 skipped keys are each the only overlap for their nets, so the aliasing never fires on this corpus. It's latent.

I considered dropping the change for that reason. I kept it because the defect is provable independently of whether a fixture happens to trigger it, and because a board that does trigger it would fail in a confusing way — overlaps that were never examined would look like overlaps the search had rejected.

Verification

Three tests pin the key's contract directly:

  • distinct label pairs on the same nets get distinct keys
  • the key is stable regardless of argument order
  • the key is stable regardless of pinIds order

Confirmed they bite: reverting collisionKey to the old net-only version fails the first test (2 pass / 1 fail).

Full suite 151 pass / 0 fail / 4 skip, tsc --noEmit clean, biome clean.

Diff: 1 source file (+17/-1) plus one test file. Independent of #699/#720.

@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
schematic-trace-solver Ready Ready Preview, Comment Jul 26, 2026 4:37am

Request Review

collisionKey used only globalConnNetId, so every label pair drawn from the
same two nets collapsed into a single key. Once the search gave up on one
pair, findNextCollidingPair skipped all the others silently — on the larger
boards a single net pair covers over a hundred label pairs (max 136 on
bug-report-20260706T220324Z).

Include pinIds, which identifies the individual label and stays stable
across relocations.

No board in the current corpus changes: the overlaps that remain are all
the only overlap for their net pair, so the aliasing never fires today. The
added tests pin the behaviour directly — reverting the fix fails them.
@DPS0340

DPS0340 commented Jul 26, 2026

Copy link
Copy Markdown
Author

Correcting myself on #721 — I told you in my audit comment on #719 that it "changes no board today" and flagged it as the easiest to decline. That was measured wrong, and the PR is worth more than I said.

What I measured was the final overlap count (43 before, 43 after) and concluded no effect. What I should have measured is what the key actually collapses. On current main, every skipped collision key is a net pair, and each one covers multiple label pairs:

bug-report-20260707T092615Z   key=net13::net2     → 10 × 1 label pairs
bug-report-20260706T220324Z   key=net17::net24    →  3 × 2
bug-report-20260721T221026Z   key=net23::net27    →  2 × 2
bug-report-20260721T221026Z   key=net23::net28    →  2 × 2
bug-report-20260707T141421Z   key=net8::net9      →  2 × 1

Five skipped keys across four boards, and the largest suppresses ten label pairs after the search gives up on one of them. Those nine were never examined — they just look like overlaps the solver rejected.

With this PR the keys become label-specific:

key=connectivity_net13#U1.3::connectivity_net2#LED1.2,U1.2

so abandoning one pair no longer silences the rest.

The final overlap count staying at 43 is real — the extra pairs it now examines happen to have no better placement available on these boards. So it is not a visible fix today, but "changes no board" understated it: it's a search-coverage bug where the solver was skipping work it had never done.

Sorry for the mislead. My earlier framing would have been a reasonable basis to close it, and I don't think that's the right call anymore. Still yours to decide — #718/#719 remain the ones with the largest measured effect.

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