Skip to content

feat(pcb): update placed footprints from libraries - #232

Open
TWT233 wants to merge 5 commits into
mixelpixx:mainfrom
TWT233:feat/update-footprints-from-library
Open

feat(pcb): update placed footprints from libraries#232
TWT233 wants to merge 5 commits into
mixelpixx:mainfrom
TWT233:feat/update-footprints-from-library

Conversation

@TWT233

@TWT233 TWT233 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Add update_footprints_from_library to the pcb_components toolset so an agent can refresh already placed footprints after their linked KiCad library definitions change.

The tool supports exact reference and library-ID filters, defaults to a non-mutating dry run, and reports changed domains before applying. It refreshes losslessly supported library-owned pads, graphics, fields, attributes, metadata, and 3D models while preserving board-owned instance state and pad nets.

Closes #231

Approach

KiCad 10's IPC API does not expose a native "Update Footprints from Library" command, so this implements the operation as a typed planner and merge:

  • resolve the explicitly requested live board and selected footprint libraries;
  • parse supported .kicad_mod content into IPC footprint definitions and fail closed on unsupported/lossy clauses;
  • merge library-owned content with preserved KIID, placement, side, rotation, lock state, schematic metadata, instance fields/overrides, and nets by logical pad number;
  • transform back-side definitions with KiCad's exchange-footprint semantics;
  • canonicalize KiCad protobuf round-trip defaults, ordering, polygon cycles, and equivalent enum values before deciding whether a footprint changed;
  • apply all selected updates in one UpdateItems batch inside one KiCad undo commit.

A reviewed dry run returns plan_revision; apply requires that exact revision. Any selected conflict or stale revision prevents the batch from being sent.

Compatibility and safety

  • Adds one public MCP tool; existing tools and arguments are unchanged.
  • Operation is live-KiCad-IPC-only and validates the requested active board. There is no closed-board text-editing fallback.
  • dry_run defaults to true; dry_run=false requires expected_plan_revision from a current plan.
  • Missing libraries, duplicate/missing selected references, unsupported footprint content, removed connected pads, and stale board/library inputs return conflicts without mutation.
  • Apply uses one batch and one undo transaction; an IPC update failure drops the commit rather than leaving a partial refresh.
  • Layer handling now composes with [BUG] Dwgs.User footprint graphics can crash KiCad during live placement #237: footprint refresh routes through build_footprint_item, so root, pad, and graphic layers use try_layer_from_name and an unrepresentable layer is refused before anything reaches KiCad.
  • Tool counts and bundled library/PCB workflow guidance are updated to 204 registered tools / 210 including meta-tools.
  • Rollback is a normal KiCad undo for an applied operation, or reverting this PR for the public tool addition.

Current branch status

Rebased-equivalent merge commit 5f330fe brings the branch onto post-v0.6.1 main, including #229, #230, and #237. The PR is CLEAN / MERGEABLE at head 5f330fe90e6ec513c76dc38044b679bdb8b6ee38.

Validation

Local validation on this branch:

  • cargo fmt --all -- --check
  • cargo clippy --workspace --locked --all-targets -- -D warnings
  • cargo test -p konnect-core --locked pcb_footprint_update::tests (21 passed)
  • cargo test --workspace --locked --lib --tests -- --skip update_symbols_from_library_refreshes_a_stale_embedded_copy --skip update_symbols_from_library_refuses_a_moved_pin_unless_allowed --skip update_symbols_from_library_refuses_a_removed_pin
  • cargo test --workspace --locked --doc
  • git diff --check

The three skipped update_symbols_from_library_* tests fail identically on clean upstream/main@3827cdd on this host, so they are not introduced by this PR.

GitHub CI for head 5f330fe is green:

  • Check & Test: ubuntu, macOS, Windows
  • Clippy
  • Format
  • Nix flake
  • Schematic viewer
  • Plugin (Python)
  • PCM packaging validation

Live/board acceptance from the original feature work:

  • Disposable KiCad 10 live test: cargo test -p konnect --test live_kicad_tools footprint_library_update_apply_then_dry_run_is_noop --locked -- --ignored --nocapture (1 passed). It created and placed a fixture, changed its library pads/graphics/metadata/model, completed dry-run -> apply -> noop, then ran DRC.

Review checklist

  • The diff is focused and contains no generated output, personal data, or unrelated cleanup.
  • New names follow docs/NAMING_CONVENTIONS.md; no existing public name changes require migration handling.
  • New behavior and failure paths have regression coverage.
  • File/library reads fail closed; live PCB mutation preserves unrelated content.
  • IPC mutations verify the requested board and do not leave partial batches.
  • Registry, tool-directory.md, DEV.md, README, plugin, and packaging counts are synchronized.

TWT233 and others added 4 commits August 16, 2026 15:28
Co-authored-by: TRAE CLI <noreply@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com>
mixelpixx added a commit that referenced this pull request Aug 17, 2026
`builders::layer_from_name` mapped anything it did not recognise to
`BL_UNDEFINED`, and its table stopped at `In2.Cu` — no `Dwgs.User`,
`Cmts.User`, `Eco1/2.User`, `F/B.Adhes`, `Margin`, `Rescue`, no inner
copper past the second, and none of `User.1`–`User.45`.

`Dwgs.User` is the one that mattered. It is ordinary content in the
official libraries, and KiCAD 10.0.5 does not validate a scalar layer
field on an incoming item: it indexes its layer bitset with whatever
arrives. So placing
`Connector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal`
(two `Dwgs.User` children) or
`Connector:BJB_Pico_46.110.1001_Receptacle_Horizontal` (eight) faulted
KiCAD at `0xc0000005` in `kicommon.dll`, discarding the session's
unsaved board. Konnect saw only an NNG receive timeout.

Two halves, because widening the table alone would leave the next layer
KiCAD adds able to do this again:

* `layer_from_name` now covers every layer a KiCAD 10 footprint can
  legally draw on. Inner copper and user layers are computed rather than
  listed — the enum is contiguous for both runs except that
  `BL_Rescue = 62` sits between `BL_User_9 = 61` and `BL_User_10 = 63`.

* `try_layer_from_name` refuses a name with no representation, and
  `build_footprint_item` now validates the root layer, every pad layer
  and every graphic layer before it builds a single child. An
  unrepresentable layer has to stop where it can still be reported;
  downstream there is no error to catch, only a dead editor.

The pad path already dropped `BL_UNDEFINED` from its layer list; the
graphic and text paths passed it straight through. That asymmetry was
the bug.

Reported with a full root-cause analysis by @neusse. The widened table
follows the arithmetic in @TWT233's #232, which arrived at the same
mapping while building `update_footprints_from_library`.

Refs #237
mixelpixx added a commit that referenced this pull request Aug 17, 2026
Two defects in v0.6.0, both of which could cost a user work.

**#237 — KiCAD could be terminated while placing a footprint.** An
ordinary `Dwgs.User` graphic serialised as `BL_UNDEFINED`, and KiCAD
10.0.5 indexes its layer bitset with whatever arrives rather than
validating it. Two official library footprints reproduce it; the
process exits and takes any unsaved board with it. The layer table now
covers every layer a KiCAD 10 footprint can draw on, and an
unrepresentable layer is refused before anything is sent. Reported with
a full root-cause analysis by @neusse; the widened mapping follows the
arithmetic in @TWT233's #232.

**#238 — `konnect init --help` ran the installer.** Subcommands were
matched before the `--help` arm and the client parser skipped every
argument that was not `--client`, so a command that reads as
documentation overwrote `~/.claude`. `--help` is now answered wherever
it appears, and an unrecognised argument is an error rather than being
ignored. Reported by @neusse.

Known limitation, filed as #240: after KiCAD dies for any reason, the
next closed-board call still cannot tell "KiCAD was never running" from
"KiCAD died a second ago holding this board", and edits the file.

No tool added, removed or renamed; no response shape changed.
19 toolsets, 202 tools.
Rebase-equivalent merge onto post-v0.6.1 main, including mixelpixx#229 and mixelpixx#230.
The combined catalogue is pcb_components 17, 204 registered + 6 meta =
210 total; README, DEV.md, tool-directory.md and package metadata move
together and doc_tool_counts verifies the arithmetic.

mixelpixx#237 independently landed the layer mapping this PR introduced. Keep main's
fallible try_layer_from_name boundary and aliases, discard the duplicate map,
and route footprint refresh through build_footprint_item so root, pad and
graphic layers are refused before an unrepresentable value reaches KiCad. A
refresh-specific In99.Cu regression test pins that composition.

Preserves main's closed-board move, rotate and flip tools while adding
update_footprints_from_library to the same toolset and Skill catalogue.

Gate: fmt; clippy --workspace --locked --all-targets -D warnings; all 21
footprint-refresh tests; workspace lib/tests excluding three
update_symbols_from_library tests that fail identically on clean
upstream/main@3827cdd on this host; doctests.

Co-authored-by: TRAE CLI <noreply@bytedance.com>
@mixelpixx

Copy link
Copy Markdown
Owner

Status at Round 12: your main-merge resolved the layer_from_name reconciliation cleanly — thank you for taking that on before I got to it. The branch is conflict-free against main as of v0.7.0. What remains on my side before merge: the three review nits (the module-wide #[allow(dead_code)], the tcp://never-dialed client built to reach a pure builder, and preserved defaulting rather than deriving) plus a live run of update_footprints_from_library against a KiCAD session — this tool rewrites placed footprints, so it gets the same live gate flip_component got. That's scheduled next round; nothing needed from you.

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.

Add live Update Footprints from Library support

2 participants