Skip to content

SYN-40 - A JLCPCB tier that can drill the holes a module footprint ships - #254

Merged
SammyOina merged 7 commits into
absmach:mainfrom
JeffMboya:SYN-jlc-advanced-drill
Nov 2, 2026
Merged

SammyOina merged 7 commits into
absmach:mainfrom
JeffMboya:SYN-jlc-advanced-drill

Conversation

@JeffMboya

Copy link
Copy Markdown
Contributor

What type of PR is this?

This is a feature

What does this do?

Adds jlc-advanced: jlc-filled-via with a 0.2 mm minimum drill. Nothing else moves off the standard floor.

Which issue(s) does this PR fix/relate to?

Answers the declared-process finding on #184. No profile could drill the ESP32 paddle vias.

Have you included tests for your changes?

Yes, three: only the drill moves, the paddle geometry fits, and jlcpcb still resolves to jlc-standard.

Did you document any new/modified features?

Yes, on jlc_advanced and in the capability descriptor's fab_profiles and profiles_offering_via_in_pad.

Notes

All five KiCad ESP32-WROOM-32 footprints drill twelve 0.2 mm paddle vias on 0.6 mm pads.

JeffMboya added a commit to JeffMboya/synth that referenced this pull request Oct 21, 2026
Byte-for-byte from `origin/SYN-jlc-advanced-drill`. Every KiCad
ESP32-WROOM-32 footprint drills twelve 0.2 mm thermal vias into the
ground paddle, and no profile could make them: jlc-standard,
pcbway-standard and jlc-filled-via floor at 0.3 mm, oshpark-4layer at
0.25 mm. The board below carries that module, so without this it
declares a process it cannot be built on.

Identical to absmach#254, so whichever lands first the other is a no-op.

@SammyOina SammyOina left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Enforce the profile's supported layer count

The capability descriptor advertises jlc-advanced for [4, 6] layers, but the runtime profile resolution uses only the manufacturer string. ManufacturerProfile has no layer applicability, so a 2-layer board can select jlc-advanced, pass the 0.2 mm/via-in-pad checks, and be treated as fabrication-ready even though it does not match the declared process.

Please validate the selected profile against board.layers before routing/DRC and add a regression test showing that jlc-advanced is rejected on a 2-layer board. The descriptor alone is not sufficient to enforce this constraint.

@JeffMboya
JeffMboya force-pushed the SYN-jlc-advanced-drill branch from 80a70db to a7a2994 Compare October 22, 2026 15:18
@JeffMboya

Copy link
Copy Markdown
Contributor Author

Fixed in a7a2994. ManufacturerProfile now carries supported_layers, and E-SYNTH-PROCESS-004 refuses jlc-advanced on 2 layers at ERC. Stock tiers stay unconstrained.

@SammyOina

Copy link
Copy Markdown
Collaborator

[P1] Enforce layer applicability in all fabrication/DRC consumers

E-SYNTH-PROCESS-004 checks supported_layers only through the validator, but direct DRC/routing consumers still call ManufacturerProfile::from_name() and use the jlc-advanced 0.2 mm geometry without checking supports_layers(). A 2-layer board declaring jlc-advanced can therefore still pass the direct DRC/profile path as if that process were available. Please enforce layer applicability wherever the profile is consumed for fabrication/DRC gating, including CLI and MCP paths, or return a structured unsupported-process error. Add a regression test through the direct DRC path, not only the validator.

@JeffMboya

Copy link
Copy Markdown
Contributor Author

Fixed in 20a11cb. ManufacturerProfile::for_layers checks the stackup at resolution and falls back to jlc-standard. All six fabrication and DRC consumers now use it, not from_name.

@SammyOina

Copy link
Copy Markdown
Collaborator

[P1] Constrain jlc-filled-via to its advertised layer counts

The new layer-aware resolver does not constrain jlc-filled-via: the capability descriptor advertises it for layers [2, 4], but ManufacturerProfile::jlc_filled_via() leaves supported_layers empty. Consequently, for_layers("jlc-filled-via", 6) still returns the filled-via profile and enables its via-in-pad geometry, while E-SYNTH-PROCESS-004 does not report anything because an empty list means “all layers.” Please set jlc-filled-via.supported_layers to [2, 4] and add a regression test rejecting it on a 6-layer board. Also reconcile the capability descriptor’s layer lists with the resolver’s supported-layer behavior for the stock profiles so the advertised fabrication capabilities and direct DRC resolution cannot disagree.

JeffMboya and others added 5 commits October 30, 2026 18:02
…tprint ships

Every KiCad ESP32-WROOM-32 variant — plain, D, E, U and UE — drills
twelve 0.2 mm thermal vias into the ground paddle. No profile could
make them. `jlc-standard`, `pcbway-standard` and `jlc-filled-via` floor
at 0.3 mm and `oshpark-4layer` at 0.25 mm, so a board carrying that
module declared a process it was knowingly violating, which is a
qualification failure rather than a rounding error.

`jlc-advanced` is the tier JLCPCB sells for exactly this: the 0.2 mm
minimum via hole on four- and six-layer builds. It is built on
`jlc-filled-via` rather than on the plain tier because those holes sit
inside pad 39, so they need the fill as well as the drill.

Only the drill moves. Trace width, clearance, edge clearance and the
annular ring stay on the standard floor, for the reason
`jlc-filled-via` already records: a board that names a tier to reach one
capability is not also handed geometry it never paid for. The ring in
particular did not have to move — the paddle's vias sit on 0.6 mm pads,
a 0.2 mm ring, which already clears the 0.13 mm floor. The drill was
the only thing in the way.

Matched exactly, like `jlc-filled-via`, so `jlcpcb` and `JLCPCB
advanced` both still resolve to the conservative floor. A board gets the
tier it named or `jlc-standard`, never a surcharged process by accident;
there is a test for each of those spellings.

Also listed in the capability descriptor, under `fab_profiles` and
`profiles_offering_via_in_pad`, so `synth capability` does not advertise
a smaller set of processes than the code offers.
The capability descriptor advertised `jlc-advanced` for 4 and 6 layers, and
nothing enforced it. `ManufacturerProfile` carried no layer applicability and
resolution matched on the manufacturer string alone, so a 2-layer board could
declare the tier, clear the 0.2 mm drill and via-in-pad checks against a floor
no fabricator had agreed to, and be reported fabrication-ready.

`supported_layers` puts the restriction on the profile itself, and
`E-SYNTH-PROCESS-004` enforces it at ERC, before routing or DRC. Empty means
offered on every stackup, which is what the three stock tiers are and what
every profile TOML written before this field meant: their floors are the
fabricator's baseline and do not depend on how many layers are stacked.

Six tests. The profile knows which counts it is sold for and the stock tiers
stay unconstrained; the rule refuses `jlc-advanced` on 2 layers naming the
stackup in the finding, accepts it on 4 and 6, leaves every stock tier alone
on 2, 4 and 6, and says nothing about an undeclared manufacturer — that is
`E-SYNTH-PROCESS-002`'s job, which already resolves an unstated tier the same
way the router and the DRC do.
`E-SYNTH-PROCESS-004` reports the mismatch, but it only fires through the
validator. Every other consumer resolves the profile for itself and took the
floors `from_name` returned: the exporter's `.kicad_dru`, the ampacity width
in `pcb.rs`, `synth route`, `synth export-kicad` and the MCP
signal-integrity tool. A 2-layer board declaring `jlc-advanced` was therefore
judged against a 0.2 mm drill and via-in-pad on the direct DRC path and could
be reported fabrication-ready against a process no fabricator sells.

So the refusal belongs in resolution, not in a rule those paths do not run.
`for_layers` resolves the name and then checks the stackup, falling back to
`jlc-standard` when the pairing is not offered -- the same conservative
answer an unrecognised name gets, for the same reason. The fallback is
visible rather than silent: `DrcReport` carries the profile's name, and it
reads `jlc-standard` where the board said otherwise.

All six fabrication and DRC consumers now go through it. `from_name` stays
for the question it actually answers, which is what a name means.

Two tests: the advanced tier resolves to the conservative floor on 2 layers
and keeps its 0.2 mm drill and fill on 4 and 6; and every stock tier,
including an unrecognised name, resolves identically on 2, 4 and 6, so boards
that were building before the restriction existed are not downgraded.
…nst the layer count

The previous commit sent every fabrication consumer through `for_layers`,
but two still resolved the profile by name alone:

- `RouteRequest::use_profile_floor`, which sets the router's drill,
  clearance and ring floors and the via-in-pad capability for the CLI and
  all three MCP routing paths. A 2-layer board naming `jlc-advanced` was
  routed, escaped and gated against a 0.2 mm drill and a fill the tier is
  not sold with at that count, and `requires via_in_pad` was granted.
- the board setup `pcb.rs` writes into the exported `.kicad_pcb`, added on
  main after this branch was cut.

`use_profile_floor` now takes the layer count and resolves through
`for_layers`, and each caller passes `board.layers`. A router test pins the
2-layer and 4-layer answers for `jlc-advanced`.
absmach#164 (component `escape` blocks) already claims E-SYNTH-PROCESS-004 for an
unknown escape method and 005 for an escape layer the board does not have,
with diagnostic pages and fixtures for both. This branch used 004 for an
unrelated rule, so whichever merged second would give one code two
meanings and two pages at the same path.

The tier-not-offered-for-this-stackup rule moves to 006: the code, its
diagnostic page, the doc comments that cite it and the test.
@rodneyosodo
rodneyosodo force-pushed the SYN-jlc-advanced-drill branch from 20a11cb to 4ceadd1 Compare October 30, 2026 15:07
@SammyOina

Copy link
Copy Markdown
Collaborator

Review — changes requested

CI passes and the earlier jlc-advanced layer-applicability issue is fixed.

One blocking issue remains:

[P1] jlc-filled-via is not constrained to its advertised layer counts. The capability descriptor advertises this process for 2- and 4-layer boards, but ManufacturerProfile::jlc_filled_via() leaves supported_layers empty. Therefore for_layers("jlc-filled-via", 6) still returns a via-in-pad-capable profile, and the validator/direct DRC path can accept a process outside its advertised capability.

Please set jlc-filled-via.supported_layers to [2, 4] and add a regression test rejecting it on a 6-layer board. Also verify that the capability descriptor and runtime resolver report the same supported layers.

I would not approve until this mismatch is fixed.

@SammyOina

Copy link
Copy Markdown
Collaborator

Follow-up required — add the filled-via layer regression test

The latest commit correctly sets jlc-filled-via.supported_layers = [2, 4], and the current CI checks pass.

The remaining gap is test coverage: please add a regression test proving that for_layers("jlc-filled-via", 6) does not return the via-in-pad-capable profile, and that the validator/direct DRC path cannot accept it on a six-layer board. Also assert that the capability descriptor and runtime resolver expose the same supported layer counts.

Once this coverage is added and passes, the PR should be ready for approval.

@rodneyosodo

Copy link
Copy Markdown
Contributor

Added regression test: jlc-filled-via is offered on 2/4 layers only and resolves to the conservative floor on 6 layers. Descriptor already lists [2,4].

@SammyOina
SammyOina merged commit 58ac0f7 into absmach:main Nov 2, 2026
5 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.

3 participants