Skip to content

feat(ecad): fab-prep as one command, with the DRC delta as its receipt#678

Open
ecto wants to merge 1 commit into
mainfrom
claude/mystifying-jackson-c38bce
Open

feat(ecad): fab-prep as one command, with the DRC delta as its receipt#678
ecto wants to merge 1 commit into
mainfrom
claude/mystifying-jackson-c38bce

Conversation

@ecto

@ecto ecto commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Turns the manual CM5 "fab prep" sequence — certify the remaining connections, calibrate the rules, census the offending nets, strip them, re-route, repeat, prune the dead copper, export — into one reproducible command. New crate crates/vcad-ecad-fabprep, exposed as vcad fab-prep and the fab_prep MCP tool.

vcad fab-prep routed.pcb.json -o out/ --calibrate-rules

The receipt is the point

On an imported fixture "zero DRC violations" is not achievable, and anyone reporting it is reporting something else — the CM5 board stripped of all routing already scores 1,546 violations against its own land patterns. So the report never gives one number. It gives the pair, and charges only the difference to the router:

rule                     baseline      final route-attributable  mode
------------------------------------------------------------------------------
Clearance                      74         74                  0  set difference
CourtyardOverlap               23         23                  0  set difference (not loop-fixable)
MinTraceWidth                   0         69                 69  set difference *** ACCEPTED BY WAIVER ***
NetIslands                     30         18                  0  count difference (not loop-fixable)
SameNetBypass                   0          4                  4  set difference *** ACCEPTED BY WAIVER ***
Short                         906      1,315                  0  set difference
UnconnectedNet                408        252                  0  count difference (not loop-fixable)
UnstitchedPad                 105         58                  0  count difference (not loop-fixable)
------------------------------------------------------------------------------
TOTAL                       1,546      1,813                 73

How the difference is taken is stated per rule rather than assumed: a set difference for geometric rules whose identity survives a strip, a count difference for connectivity rules that adding copper legitimately rewrites (there the claim is the weaker, non-fictional one — the router left fewer than it found). drc_report.txt tags every line ROUTE / WAIVED / FIXTURE.

Two attribution subtleties the naive delta gets wrong

  • Shorts propagate. If the fixture's own overlapping pads short X–A and Y–A, routing net A between its own pads merges the two copper blobs and the DRC now also reports X–Y. That pair is new, but the router did nothing wrong. A final short is charged only when it bridges two baseline short groups. On CM5 this is the difference between reporting 409 route-attributable shorts and the honest 0.
  • The loop can "fix" DRC by deleting copper. Every geometric violation goes away if you remove the trace, and a board with no traces scores perfectly. Connectivity is measured on arrival and on completion, and a run that bought cleanliness with unconnected nets does not converge. The loop also keeps the best board it saw and restores it, so a wandering run never hands back something worse than it was given.

Rule calibration: opt-in, floored, logged

Only relaxes a rule to the point where the board's own given geometry stops being illegal — declared via classes, and pre-existing pad drills. Never router-placed vias, which would let the routing justify its own rules. Every change carries its derivation:

minDrill 0.200 -> 0.120
    minDrill 0.120 from via class 'Default' (0.210/0.120 mm), realized by 656 vias
    — the board's global minimum (0.200) forbids the via class the board itself declares

Hole-to-hole is deliberately not inferred from a via class: it is a process capability, not something a class declares. If a board's declared value is wrong for its fab, the fix is to state the correct rule on the board — a decision with a named owner.

Waivers

Real fab packages ship with real exceptions; the CM5 campaign accepted 69 fab-legal 0.08 mm neckdowns in prose. --accept MinTraceWidth makes that explicit — waived violations stay counted, stay listed, and the waiver is named in FAB_NOTES.md. An unrecognised rule name refuses the run rather than silently accepting nothing.

Fail closed

A non-converging run writes the receipt and the board, withholds every fabrication file, and exits 1. export_gerber's clean-DRC gate is untouched — fab-prep is the supported way to get clean, not a way around it.

Acceptance

vcad fab-prep ~/Downloads/cm5-vcad-fab/board.pcb.json --calibrate-rules --accept MinTraceWidth --accept SameNetBypass reproduces the reference package byte-for-byte across all 27 fabrication artifacts (21 gerbers, drill.drl, board.kicad_pcb, bom.csv, pick_place.csv, board.svg), replacing the hand-written FAB_NOTES.md / drc_report.txt with the generated receipt and adding machine-readable fab_report.json.

Tests cover the convergence loop, the fail-closed path (write_fab_package refuses and leaves the evidence), the clean-by-deletion refusal, the best-board guard, short propagation, calibration derivation/floor/refusal, and waiver handling — 31 Rust unit tests plus 3 MCP tool tests.

Verification

  • cargo test --workspace — green except a pre-existing vcad-kernel-physics build failure (Joint: Default in m11_adjoint_rollout) caused by uncommitted drift in the sibling phyz checkout, unrelated to this change. --exclude vcad-kernel-physics: 2,619+ tests, 0 failures.
  • cargo clippy --workspace --exclude vcad-desktop -- -D warnings — clean.
  • cargo fmt --all --check — clean.
  • npm test -w @vcad/mcp — 937 passed.

Note: packages/kernel-wasm/vcad_kernel_wasm* was rebuilt locally to exercise the new ecadFabPrep binding, then restored from origin/main per the single-writer policy. CI builds WASM from source, so the TypeScript job exercises the binding.

🤖 Generated with Claude Code

Taking a routed board to a manufacturable Gerber package was a hand-run
sequence of example binaries: certify the remaining connections, calibrate the
rules, census the offending nets, strip them, re-route, repeat, prune the dead
copper, export. New crate `vcad-ecad-fabprep` is that sequence, exposed as
`vcad fab-prep` and the `fab_prep` MCP tool.

The receipt is the point. On an imported fixture "zero DRC violations" is not
achievable and anyone reporting it is reporting something else: the CM5 board
stripped of all routing already scores 1,546 violations against its own land
patterns. So the report never gives one number. It gives the pair — stripped
baseline and finished board — and charges only the difference to the router,
stating per rule how the difference was taken (set difference for geometric
rules whose identity survives a strip; count difference for connectivity rules
that adding copper legitimately rewrites).

Two attribution subtleties the naive delta gets wrong, both fixed here:

- Shorts propagate. If the fixture's own overlapping pads short X–A and Y–A,
  routing net A between its own pads merges the blobs and X–Y is newly
  reported — a pair the router did not cause. A final short is charged only
  when it bridges two *baseline* short groups.
- The loop can "fix" DRC by deleting copper. Every geometric violation goes
  away if you remove the trace, and a board with no traces scores perfectly.
  Connectivity is now measured on arrival and on completion, and a run that
  bought cleanliness with unconnected nets does not converge.

Rule calibration is opt-in and logged: it only relaxes a rule to the point
where the board's own *given* geometry stops being illegal (declared via
classes; pre-existing pad drills — never router-placed vias, which would let
the routing justify its own rules), is floored at laser-microvia limits, and
records its derivation. Hole-to-hole is deliberately not inferred from a via
class — it is a process capability, not something a class declares.

Waivers (`--accept MinTraceWidth`) make the exceptions a real fab package
ships with explicit instead of prose: waived violations stay counted, stay
listed, and are tagged WAIVED in the dump. An unrecognised rule name refuses
the run rather than silently accepting nothing.

Fails closed throughout: a non-converging run writes the receipt and the board,
withholds every fabrication file, and exits 1. `export_gerber`'s clean-DRC gate
is untouched — fab-prep is the supported way to get clean, not around it.

Verified against the manual result: `vcad fab-prep board.pcb.json
--calibrate-rules --accept MinTraceWidth --accept SameNetBypass` reproduces
~/Downloads/cm5-vcad-fab byte-for-byte across all 27 fabrication artifacts
(21 gerbers, drill.drl, board.kicad_pcb, bom.csv, pick_place.csv, board.svg),
replacing the hand-written FAB_NOTES.md/drc_report.txt with the generated
receipt and adding machine-readable fab_report.json.

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.

Project Deployment Actions Updated (UTC)
vcad-mcp Building Building Preview, Comment Jul 25, 2026 3:30am
3 Skipped Deployments
Project Deployment Actions Updated (UTC)
mecheval Ignored Ignored Jul 25, 2026 3:30am
vcad Ignored Ignored Jul 25, 2026 3:30am
vcad-docs Ignored Ignored Jul 25, 2026 3:30am

Request Review

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