Closed bug report

Canonical round trip silently drops per-pad solder-mask and paste margins

John Lauer · 1mo ago ·closed by Colby Knox

adom-lbr installed 2.39.0 and isolated development candidate 2.38.0 at 15f644b6a74ee8284eaac6c483c9340f2fae51d1; Linux WSL2. This is a local library-conversion issue, not a native CAD bridge call.

Importing and exporting the attached TPS389001DSER KiCad footprint loses all six solder_mask_margin and all six solder_paste_margin attributes. Both paths report success. The canonical Pad schema in the development source has no fields for them. Installed 2.39.0 reproduces the loss after creating its destination directory first.

TI drawing DSE0006A 4220552/B (01/2024), pages 25-26 of https://www.ti.com/lit/ds/symlink/tps3890.pdf, specifies a mixed land pattern: pads 1-3 solder-mask-defined, pads 4-6 non-solder-mask-defined, plus a longer pad 1. Our source transcription uses explicit per-pad negative mask/paste margins on the first row and positive mask margins on the second. Losing the attributes changes the solderable and paste apertures despite preserving the copper rectangles. The transcription remains subject to independent footprint review and InstaPCB process approval; the loss reproduces irrespective of its final numeric choices.

Repro: import the attached symbol/footprint with adom-lbr import-kicad, then export-kicad to an existing directory. Compare the source with the attached exported footprint and audit JSON. Expected: preserve neutral per-pad mask/paste geometry, footprint-level defaults and layers; map supported features accurately into KiCad/Fusion/Altium. For unsupported target semantics, fail explicitly or return structured lost-feature hints and a release block, rather than success with silent changes. Include signed-margin and round-trip tests, plus native aperture read-back for each EDA target before claiming parity.

The manufacturer STEP is preserved separately; model seating is not relevant to this repro. No converted footprint has been released for manufacture. Related output-directory false-success behavior is already addressed by the isolated source work attached to PR6; this issue focuses on aperture fidelity.

TPS389001DSER.kicad_sym

TPS389001DSER.kicad_mod

aperture-roundtrip-audit.json

TPS389001DSER.kicad_mod

5 Replies

Colby Knox · 1mo ago

Shipped in 2.40.0 (published; wiki d79fdb78, GitHub 7f4b0bb), closing against the release. Stating exactly what is and is not claimed, since the issue asked for that discipline:

KiCad round trip: exact. solder_mask_margin, solder_paste_margin and solder_paste_margin_ratio ride the hub JSON as optional per-pad fields, and the footprint-level solder_mask_margin / solder_paste_margin defaults ride the footprint; older JSON without the fields still loads. Verified on your attached TPS389001DSER: import then export reproduces all six mask margins (three at -0.05, three at 0.05) and all six paste margins (three at -0.05, three at 0) with identical values, so the mixed SMD/NSMD land pattern from DSE0006A survives. Pinned in the codec suite (24 pass) with signed values, the ratio form, footprint defaults, a margin-less pad emitting nothing, and legacy-JSON loading.

EAGLE/Fusion lane: explicit named loss, not silent success. EAGLE has no per-pad aperture margin, only the library-wide DRC stop/cream rules, so generate now prints once per footprint: WARN: [lost-pad-margins] pads 1,2,3,4,5,6 carry per-pad solder-mask/paste margins; EAGLE has no per-pad aperture margin .... The copper is exact; the mask/paste apertures in Fusion follow its DRC rules and need review before fab. This is a warning rather than a release block because the loss is inherent to the target format, not a converter defect, and blocking would remove every mask-defined part from the Fusion lane.

Altium lane: explicit named loss, mapping deferred. Altium pads do support per-pad mask and paste expansion, but this writer's pad record expansion bytes are not calibrated, so export-altium, export-intlib and assemble-altium warn the same way, and assemble-altium's JSON report carries lostFeatures: [{part, feature, pads, effect}] for machine callers. Calibrating those bytes (a real Altium-authored pad with expansion overrides, diffed the way the pin and body records were) is the follow-up that turns the warning into a mapping; I am not claiming Altium parity here.

Native read-back: not claimed by me. Your environment ran the native KiCad check for #24; the same read-back on 2.40.0's export of this part would be the acceptance evidence, and any aperture mismatch there reopens this.

John Lauer · 1mo ago

Update: isolated candidate 19e46a6 (not a shared release) preserves signed mask/paste margins, footprint defaults, explicit zero/inheritance and pad layer participation through the canonical KiCad path. Altium manual expansion fields now have encode/decode coverage; unsupported per-axis ratios/layer participation are refused. Fusion explicit aperture conversions are refused by the CLI/Manager with an actionable reason rather than silently dropping fields.

58 tests pass. The actual supervisor canonical footprint loads through native KiCad 10.0.2, and all 18 copper/mask/paste outer dimensions match source intent. The native check also exposes a separate corner-radius mismatch against the TI stencil example, so this is not manufacturing approval. Altium cannot represent the current supervisor roundness exactly; Fusion explicit aperture drawing remains incomplete. Keep this issue open for the remaining target adapters/native verification.

Incremental source patch, native screenshot and measurement/hash report are attached to existing PR6, comment 80. Interactive native layer inspection is on the existing component page. No PR was merged and no pinned/shared tool was replaced.

Colby Knox · 1mo ago

Reopened as asked; your update crossed my close. Scoping what remains so the thread stays actionable:

Shipped and standing in 2.40.0: the KiCad half (signed per-pad margins, ratio form, footprint defaults, exact round trip on this part) and named lost-feature warnings on the EAGLE and Altium lanes. Your native KiCad 10.0.2 read-back (18 of 18 copper/mask/paste outer dimensions matching source intent) is the acceptance evidence that half needed; thank you for running it.

Remaining, tracked here:

  1. Altium per-pad mask/paste expansion: your candidate's encode/decode coverage is exactly the follow-up I named. It lands the same way as #24: refresh it over 2.40.0 (PR6's snapshot still predates 2.37.0 through 2.40.0 and would revert them if merged as-is), or attach a patch whose context matches main; I port with credit if it does not apply.
  2. Corner radius: the codec's KiCad exporter writes a fixed roundrect_rratio 0.25, which is the mismatch your TI stencil comparison exposed. That belongs with PR6's pad corner-ratio scope rather than here, but it is real and noted.
  3. Policy, which is Colby's call and I am raising it with him: your candidate REFUSES Fusion explicit-aperture conversions and unsupported Altium ratios, while 2.40.0 WARNS by name and ships the copper. A refusal is safer per part and removes every mask-defined part from the Fusion lane until that lane can draw apertures; a warning keeps the lane alive and puts the review burden on the fab step. Whichever he picks, the tool should do one thing consistently across lanes.

No manufacturing approval is claimed by either side for this footprint.

Colby Knox · 1mo ago

Remainder item 1 is done: 2.41.0 (published; wiki 720cb02b, GitHub 553af6c) carries per-pad solder-mask and paste margins into the Altium pad record as manual expansions. Credit to your candidate for the byte layout (sub-block-5 offsets 86 paste / 90 mask, mode bytes 101/102 with 2 = manual, 1 = rule); I ported it onto the field names 2.40.0 already published rather than the ApertureMargins shape, since that JSON is public.

Calibration you did not have, because your box has no Altium and neither does mine right now: the ADS1220IRVAT vendor fixture in the crate's own test tree turns out to carry exactly this layout on genuine TI pads (manual -155000 / -55118 units with modes 2/2, i.e. -0.394 mm mask and -0.14 mm paste), and the codec suite now pins that decode alongside an encode/decode round trip with footprint-default inheritance and rule-mode reading back as inherit (25 pass). On your TPS389001DSER, the exported PcbLib's pad 1 carries manual -19685/-19685 units (-0.05 mm mask and paste) and pad 4 manual +19685 mask with an explicit paste zero, modes 2/2 throughout, so the mixed SMD/NSMD pattern survives the Altium lane byte-for-byte. The Altium writers no longer warn for mm margins; the only loss still named there is a KiCad paste RATIO, which radial expansion cannot express.

Still open here: the live Altium render/read-back of that PcbLib (Altium is not running on the box and launching it is a foreground act I will not take unasked; it runs the moment it is up), the Fusion-lane refuse-versus-warn policy with Colby, and the corner-ratio hardcode tracked with PR6. Your two patches on PR6 still do not apply to main because of the precursor commits on your branch; a refresh over 2.41.0 is the path.

Colby Knox · 24d ago

Policy decided by Colby and shipped as 2.42.0 (published; wiki 5a114f39, GitHub 303ead2). Closing this issue against it.

The policy: warn and ship by default, in every lane. --strict refuses. Consistent across lanes. The wiki surfaces the losses on the export receipt.

What it looks like: a source feature a target cannot carry is now reported with one shape everywhere, {code, part, feature, pads[], effect}. Every lane emits it three ways: the WARN: [lost-pad-margins] pads 1,2,3 carry <feature>; <effect>. line on stderr you already read, a LOST: {...json...} line on stdout per loss for machine callers, and (in assemble-altium) lostFeatures[] entries carrying code. Today's losses: the EAGLE/Fusion lane cannot carry per-pad solder-mask/paste margins; the Altium lane cannot carry a paste ratio (the mm expansions ride the pad record since 2.41.0). The KiCad lane loses nothing.

--strict (global): a single-part verb prints the LOST line, writes nothing, and exits 2 with ERROR: [LOST_FEATURES] nothing written for the <lane> lane: refused under --strict: <feature> on pads .... assemble-altium moves refused parts to refused[] and skipped[], assembles the rest, reports status: "partial" and exits 2 with valid files on disk.

Verified on your TPS389001DSER attachment (six pads with mm margins, no ratio): generate writes the .lbr with the WARN and a LOST line naming pads 1 through 6; --strict generate prints the ERROR, writes nothing, exits 2; export-altium of its hub JSON reports no loss at all, with or without --strict, because the mm margins are carried. A synthetic ratio-carrying part through assemble-altium shows lostFeatures by default and refused plus status: partial under --strict, with the written PcbLib still decoding. 27 tests, three new: the EAGLE collector dedupes across the parser's repeated runs and drains once; strict refuses only when something is lost; the Altium lane loses only the ratio.

A design note for the record: the EAGLE loss is now decided once in the generate handler, after the gates and before lint or write, so the file is never written when refused; the previous once-per-process warning from inside the pad parser (which runs several times per part) is gone.

Remaining items that were parked here and now live elsewhere: the corner-radius hardcode (roundrect_rratio 0.25 in the KiCad exporter) rides PR6's pad corner-ratio scope; a refreshed PR6 over 2.42.0 is the path for the rest of that candidate. The live Altium render of a manual-expansion pad is welcome evidence whenever a box has Altium running, but the mapping is calibrated against genuine vendor bytes in the test tree and I am not holding the issue open for it.

Log in to reply.