fix(pcb): report an unassigned footprint instead of failing the sync - #523
Conversation
neusse
left a comment
There was a problem hiding this comment.
Please restore one duplicate-reference invariant across assigned and unassigned components before this can merge.
Exact head reviewed: 05f31557f81bdc3f47d78651d90a4fa7ccf58931; all ten hosted checks are green, and the main #507 behavior is otherwise correctly scoped and evidenced. The parser now checks duplicates only in by_reference, but an unassigned component never enters that map. Therefore unassigned → unassigned and unassigned → assigned occurrences of the same reference are accepted; plan_sync later silently drops repeated unassigned entries. That can make malformed input look plausible and can classify one board footprint through two roles.
Use one seen_references set across every exported component before branching on footprint assignment. Preserve the existing fail-closed duplicate error, and add order-sensitive regressions for unassigned → unassigned and unassigned → assigned (plus the reverse if it is not already covered). Then rerun the ten checks and reply with the new head SHA. Closes #507 remains correct once this is fixed.
05f3155 to
55e7b1b
Compare
|
Corrected and reconstructed once. Head: The invariant: every exported component is checked against one Order-sensitive regressions (the real export with one Negative control: with the Gate on this head: fmt / clippy |
neusse
left a comment
There was a problem hiding this comment.
One public-response naming correction remains on exact head 55e7b1b70d3ca28abeb2e56657c8634d8fc6e6c0.
The earlier duplicate-reference blocker is resolved and the implementation now satisfies #507, but the new top-level array is named unassigned_footprint. docs/NAMING_CONVENTIONS.md requires collection responses to use a plural noun. Please rename that JSON collection to unassigned_footprints and update its tests, migration text, tool description, and PR body consistently. The singular coverage.unassigned_footprint count category may remain singular.
Then rerun all ten checks and reply with the new exact head. Closes #507 remains correct after this focused correction.
|
Renamed and reconstructed once. Head: The top-level collection is now One thing I added rather than just renaming. The response shape had no test at all — every Negative controls on this head: Gate: fmt / clippy
|
55e7b1b to
d33780a
Compare
|
Please refresh this one focused commit onto current The plural |
|
Merge-train sequencing correction: please hold the final refresh until #528 lands, then reconstruct #523 once onto that new current |
|
#528 has landed as The earlier duplicate-reference and plural |
`kicad-cli sch export netlist` writes no `(footprint …)` node for a symbol whose Footprint property is empty, and `parse_exported_netlist` required one for every component. A single footprint-less symbol — a legitimate state for a generic Device:R whose package is not chosen yet, and until #506 every symbol Konnect itself placed — failed the whole sync with "KiCad netlist node is missing footprint": no component named, every other one blocked. The export now reads `footprint` as optional. A component without one goes to a new `unassigned` list (reference, value, lib_id from `libsource`, symbol path); a nets node naming it is dropped rather than fatal, since there is no pad to carry the net. `plan_sync` treats the list the way it treats `skipped`: a non-blocking category, never a diagnostic — any diagnostic clears the whole plan, which is the failure this removes. Each entry is reported with what the board holds for it: `absent` (nothing is added, and it is not counted under `added`) or `kept` (a footprint with that identity or reference exists and is left exactly as it is, counted as matched, the way eeschema skips "cannot update … no footprint assigned" and continues). Saved on_board / in_bom flags still apply to them. One duplicate-reference invariant covers both roles: every exported component is checked against one `seen_references` set before the footprint branch, so an unassigned repeat of an assigned reference (or the reverse) refuses exactly as an assigned repeat always did, and the plan never dedupes silently. Response: `coverage.unassigned_footprint` count pair beside `skipped_by_flag`, and a top-level `unassigned_footprint` array. Derived from the parsed export, not echoed. Additive; nothing renamed. Evidence: a real kicad-cli export with one unassigned component wired to an assigned one is the fixture (provenance in its README). Tests: parse; empty board plans two additions and lists R1 absent, status ready; an existing board footprint for R1 is kept and matched; all-unassigned is a noop with the list; a genuine conflict clears changes but keeps the list. Closes #507 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
d33780a to
7d073eb
Compare
neusse
left a comment
There was a problem hiding this comment.
Exact-head review complete for 7d073eb44969f7526d8c16890d1f8d4d1d3dd752 against current main 331d98ad021bc2fde32778e7f0c11dd411b3bd40.
Spec: pass. #507's terminal behavior, derived accounting, duplicate-reference invariant, real KiCad fixture, and focused regressions are complete.
Standards: source passes. Maintainer-side PR metadata now names the current base, shared-document overlap/order, next PR, and the correct plural-array/singular-count evidence fields. The remaining duplication/data-clump observations are non-blocking refactoring judgments outside this focused fix.
The first hosted rerun failed only because an unrelated existing mock listener encountered AddressInUse; failed/cancelled platform jobs are rerunning and must all pass before merge.
|
Refreshed onto current Diff content is unchanged from All ten required checks green on Live IPC run, the leg that was blocked on the 09-11 head. Driver PR body updated to the new head. Ready for the exact-head check. Terminal: |
Summary
kicad-cli sch export netlistwrites no(footprint …)node for a symbol whoseFootprintproperty is empty, andparse_exported_netlistrequired one for every component. One footprint-less symbol — a legitimate state for a genericDevice:Rwhose package is not chosen yet, and until #506 every symbol Konnect itself placed — failed the wholeupdate_pcb_from_schematicwith KiCad netlist node is missing footprint: no component named, every other component blocked with it.Closes #507
Approach
Report, do not refuse — eeschema's own Update PCB dialog says "footprint not assigned" per symbol and continues.
seen_referencesset before the footprint branch, sounassigned→unassigned,unassigned→assignedandassigned→unassignedrepeats refuse with the existing error exactly asassigned→assignedalways did; the plan never dedupes silently. Order-sensitive regressions are the real export with one(comp …)block duplicated by hand (kicad-cli cannot produce a duplicate).footprintis optional. A component without one goes to a newunassignedlist (reference,value,lib_idfromlibsource,symbol_path) instead ofcomponents; anetsnode naming it is dropped rather than fatal (there is no pad to carry the net). "Zero components" is only fatal when both lists are empty.plan_synctreats the list likeskipped— a non-blocking category, never a diagnostic, because any diagnostic clears the whole plan today, which is exactly the failure this removes. Each entry carries what the board holds for it:absent(nothing is added; not counted underfootprints_added) orkept(a footprint with that identity or reference already exists and is left exactly as it is — counted as matched, so not underboard_only_preserved). Savedon_board/in_bomflags still apply to them.coverage.unassigned_footprintcount pair besideskipped_by_flag, plus a top-levelunassigned_footprintsarray. Derived from the parsed export, not echoed from any argument.Branch and dependencies
Base branch:
mainat331d98a(refreshed onto #528's merge, per the #530 → #528 → #523 → #535 train; migration entry placed directly below #528's).Depends on: nothing remaining. Overlap: #535, #526, and #524 also touch
docs/API_MIGRATIONS.md; #535 and #524 also touchtool-directory.md. Their implementation hunks are independent. The explicit shared-document order is #523 -> #535; #524 and #526 reconstruct independently after their author work. Independent of #506 (sch_components.rs), which fixes the placement side; this PR is about sync behavior when an unassigned symbol exists.Series order: none — single PR, one commit.
Next PR to promote after this one: #535.
Compatibility and safety
docs/API_MIGRATIONS.mdas a minor.coverage.unassigned_footprint,unassigned_footprints.status,changes,diagnosticsand every existing count keep their names and meanings.attempt_ipc_write, one commit, post-apply read-back). An unassigned component is never sent to KiCad; a kept board footprint is not touched.cargo xtask fix-doc-counts --checkunchanged. Rollback: revert the one commit.Validation
Run on commit
7d073eb(Windows, KiCad 10.0.5 installed), exit codes captured directly; the diff is byte-identical per file tod33780a/55e7b1b, on which the same gate and theseen_referencesneuters ran:cargo fmt --all -- --checkcargo test --workspace --locked --lib --testscargo test --workspace --locked --doccargo clippy --workspace --locked --all-targets -- -D warningskicad-cli sch export netlist, and the live E2E below ran against pcbnew 10.0 over IPC. Linux/macOS not run here; hosted CI covers the matrix.Fixture from real KiCad output:
tests/fixtures/unassigned_footprint.net—kicad-cli sch export netlistof a project built through Konnect withR1(Device:R, never assigned),C1andR2(assigned),R1wired toC1. Only the(source …)path is neutralised; provenance and recipe in its README.Reproduced first, on
mainwith a release binary over MCP stdio: dry run on that project →conflicts.planned: 1, preflight KiCad netlist node is missing footprint, no plan, no component named.Negative controls (each neutered, then restored):
footprintrequired again (bail as before)a_component_without_a_footprint_is_reported_not_fatal,the_sync_goes_on_for_every_assigned_component,an_existing_board_footprint_for_an_unassigned_symbol_is_kept,an_all_unassigned_schematic_is_a_noop_with_the_list,the_unassigned_list_survives_a_conflict)seen_referencescheck disabled (review: one duplicate-reference invariant)a_repeated_unassigned_reference_is_refused,an_assigned_repeat_of_an_unassigned_reference_is_refused,an_unassigned_repeat_of_an_assigned_reference_is_refused,a_repeated_assigned_reference_is_still_refused, plus the pre-existingempty_and_duplicate_component_exports_are_rejectedthe_response_names_the_collection_in_the_plural_and_the_count_in_the_singularconflictdiagnosticthe_sync_goes_on_for_every_assigned_component,an_existing_board_footprint_for_an_unassigned_symbol_is_kept,an_all_unassigned_schematic_is_a_noop_with_the_listRestored tree: 37/37 green.
Live E2E on this head's release binary (
7d073eb), pcbnew 10.0.5 open on the project's board over IPC (drivere2e507.py; re-run 2026-09-13 after #530 restored the Windows endpoint):status: ready, 2 footprints planned (C1,R2),unassigned_footprints: [{reference: R1, value: R, lib_id: Device:R, board_state: absent}],coverage.unassigned_footprint: {planned: 1, applied: 0}status: applied, 2 planned / 2 applied,coverage.unassigned_footprint: {planned: 1, applied: 1}, same entrystatus: noop, no changes,R1still listedabsentsave_project, then the saved.kicad_pcbread backC1,R2— and noR1unassigned_footprintis absent (driver assertion)Review checklist
upstream/main, has no merge conflicts, and CI passed on this exact head.upstream/main, not a release tag.docs/NAMING_CONVENTIONS.md(unassigned_footprints,board_state); no public renames.cargo xtask fix-doc-counts --checkunchanged.Maintainer merge state
status:*workflow label.status:ready-to-mergeapplies to this exact head SHA.mainruleset.gh pr merge N --mergeafter final verification.🤖 Generated with Claude Code