← All Pull Requests

Expose complete saved-board model bindings for variant verification #10

Closed opened by John Lauer 2026-09-18

The current board model check reports only footprints with warnings, so consumers cannot verify successful bindings to their selected etched or LED variants. Add a complete modelBindings inventory containing footprint UUID, reference, layer, model paths, resolution, transforms and hidden state. Preserve existing warning/count behavior and explicitly identify the result as saved-file evidence, not unsaved editor or rendered evidence.

This addresses the inspection portion of issue #101. It does not implement native model mutation or claim render verification. The linked KiCad IPC FootprintInstance schema does not expose a model list, so mutation must not be faked by silently changing the file underneath an open editor.

Validation: 12 model-check tests passed with --no-default-features, including successful bindings, missing models, intentional model-free footprints, and an inventory unaffected by the warning limit. The default-feature build initially lacked cmake on PATH; the changed module has no IPC dependency. No shared bridge was replaced. Native Windows acceptance remains with the bridge owner before publication.

Diff Skip to comments (1)

--- a/rust/crates/kicad-core/src/model_check.rs+++ b/rust/crates/kicad-core/src/model_check.rs@@ -1,928 +1,952 @@⋯ 645 unchanged lines ⋯     };     let project_dir = p.parent().map(norm).unwrap_or_default();     let mut per = Vec::new();+    let mut bindings = Vec::new();     let (mut footprints, mut with_model, mut no_model_expected_n, mut missing, mut unresolved, mut nwarn) = (0u64, 0u64, 0u64, 0u64, 0u64, 0u64);     for fp in tree.find_all("footprint") {         let fp_id = fp.atom_at(1).unwrap_or("?").to_string();⋯ 7 unchanged lines ⋯         }         let rows = model_rows(fp, ctx, Some(&project_dir));         let a = attrs(fp);+        bindings.push(json!({+            "itemId": fp.find("uuid").and_then(|x| x.atom_at(1)),+            "reference": reference,+            "footprint": fp_id,+            "layer": fp.find("layer").and_then(|x| x.atom_at(1)),+            "models": rows,+            "noModelExpected": rows.is_empty() && no_model_expected(&fp_id, &a),+        }));         footprints += 1;         let mut w = tag_warnings(&rows);         if !rows.is_empty() {⋯ 24 unchanged lines ⋯         "counts": {"footprints": footprints, "withModel": with_model, "noModelExpected": no_model_expected_n,                    "missingModel": missing, "unresolvedModel": unresolved, "warnings": nwarn},         "footprintsWithWarnings": per,+        "modelBindings": bindings,+        "bindingsComplete": true,+        "evidenceSource": "saved-board-file",+        "unsavedEditorStateIncluded": false,         "ok": nwarn == 0,         "renderVerified": false,         "nextCheck": "kicad_show_3d_board and read the frame: bodies on every populated footprint are the proof.",⋯ 202 unchanged lines ⋯         let r = check_board(&f.ctx, &norm(&board), 1);         assert_eq!(r["footprintsWithWarnings"].as_array().unwrap().len(), 1);         assert_eq!(r["counts"]["warnings"], json!(2));+        // Warning limit must never hide successful bindings or model-free items.+        let bindings = r["modelBindings"].as_array().unwrap();+        assert_eq!(bindings.len(), 4);+        assert_eq!(bindings[0]["reference"], "U1");+        assert_eq!(bindings[0]["layer"], "F.Cu");+        assert_eq!(bindings[0]["models"][0]["exists"], true);+        assert_eq!(bindings[2]["noModelExpected"], true);+        assert_eq!(bindings[3]["models"], json!([]));+        assert_eq!(r["bindingsComplete"], true);+        assert_eq!(r["unsavedEditorStateIncluded"], false);     }      #[test]⋯ 23 unchanged lines ⋯         assert_eq!(ctx.expand("${KICAD10_3RD_PARTY}/x.step", None).0, "/custom/3rd/x.step");     } }+--- a/skills/kicad-3d-models/SKILL.md+++ b/skills/kicad-3d-models/SKILL.md@@ -1,184 +1,191 @@⋯ 181 unchanged lines ⋯ - [ ] USB-C connector visible if present - [ ] LED packages visible and arranged in expected pattern - [ ] No error dialogs in KiCad++## Inspect every saved model binding++`kicad_model_check {"boardPath":"<saved board>"}` returns `modelBindings` for every footprint, including successful bindings and intentionally model-free footprints. Each row carries the footprint UUID, reference, layer and model resolution, transforms and hidden state. `limit` truncates only the warning list, never this inventory. Compare these bindings to the selected library variants before filming.++This is explicitly `evidenceSource: saved-board-file`: unsaved editor edits are not included. Save the exact intended board first. `renderVerified` remains false; inspect the native 3D viewer to verify placement, appearance and visibility. A resolved file is not proof of correct geometry or orientation.+

Comments

John Lauer 2026-09-18

Merged and shipped in KiCad Bridge 1.0.22 (insiders). Verified on ConfRoomROG against the ESC board: 149 modelBindings for 149 footprints, 128 with models, 21 noModelExpected, while footprintsWithWarnings stays capped at 38, so the inventory is complete regardless of the warning limit. bindingsComplete:true, evidenceSource:"saved-board-file", unsavedEditorStateIncluded:false, as the PR says. Clean merge, no conflicts.

Log in to comment.