Make main the VNS working version: current upstream plus ten fixes - #180
Make main the VNS working version: current upstream plus ten fixes#180JYPochez wants to merge 22 commits into
Conversation
unescape chained four replace calls with the backslash collapse last, so an already-escaped backslash was re-read as the introducer of the next escape. KiCad escapes backslashes in property values, so a serialized `C:\\new` (the literal path `C:\new`) decoded to `C:\` followed by a real newline, and `a\\tb` to `a\` plus a tab. Any Windows path whose next character was n, t or r came back corrupted. Replaced with a single left-to-right scan. Unknown escapes now keep both characters rather than dropping the backslash, so values round-trip. Blast radius was limited to reads: the parser is read-only and all writes go through targeted text edits, so no file was ever damaged by this — the wrong string was just handed to the caller. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
resolve_lib_symbol never read sym-lib-table. It scanned a hardcoded list of install directories for a file named after the library nickname, which fails two ways, both total rather than partial. Every user library was invisible: registered in the table but living anywhere else on disk, so add_schematic_component and replace_component failed for any part not shipped with KiCad. And KiCad's own libraries were invisible on any non-standard install — the macOS candidates are /Applications/KiCad, /usr/local/share/kicad and ~/Applications, so a bundle kept anywhere else makes find_symbol_dirs() return empty and *no* symbol resolves at all. The scan also assumed nickname == filename, which KiCad never requires: a table may register .../TM16xx.kicad_sym under the nickname JY-TM16xx. Footprints already resolved through the lib-table (mixelpixx#61). Symbols did not. sym-lib-table is now consulted first, with the directory scan kept as a fallback for setups with no table. The reader follows (type "Table") indirection depth-bounded, expands ${KIPRJMOD} against the table's own directory, expands exported environment variables, and expands user path variables from kicad_common.json — those are set in Preferences > Configure Paths, are not process environment variables, and are the normal way to write a portable table. ${KICAD*_SYMBOL_DIR} falls back to the install root recovered from the table's own location. Verified live: JY-TM16xx:TM1637, JY-KYX3561AS:3661BW, MCU_WCH_RiscV:CH32V003FxPx, Device:R and power:GND all resolve where previously none did. 430 tests pass (8 added); clippy --workspace -D warnings clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A multi-unit symbol — a 74HC14, an op-amp, anything eeschema splits into gates — is placed as one instance per unit, all sharing the reference. batch_connect_to_net took the first instance matching the reference and transformed every requested pin by that instance's placement, so asking for a pin owned by unit 2 put the label on unit 1's pin. Two different nets then occupied one coordinate and were silently shorted. No error, no warning: the tool reported success with a plausible-looking position. Caught in real use — X_CLK_IN and X_DATA_IN both landed on U6 pin 1 while wiring a 74HC14 scale buffer, and only a coordinate that looked wrong gave it away. Now every instance sharing the reference is searched and the pin is resolved against the one whose unit owns it, via extract_lib_pins_for_unit, so the instance lookup and the pin transform come from the same unit. The test places two units 15.24mm apart, each owning a pin at the same local coordinate, and asserts the resolved positions differ and match their own unit's placement — which fails against the old code. 439 tests pass (2 added); clippy --workspace -D warnings clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ymbol
find_symbol_instance_block returns the first (symbol ...) block matching a
reference. A multi-unit part is one block per unit, all repeating that
reference, so three handlers that mean "the component" were silently acting
on unit 1 alone:
- batch_edit_schematic_components wrote Value/Footprint/custom fields into
unit 1 only, leaving a part claiming a footprint in one unit and none in
the rest. eeschema keeps these copies identical; the file was left
internally inconsistent. Found assigning SOIC-14 to a 74HC14: 1 of 7
units took it.
- batch_delete_schematic_components and batch_delete removed unit 1's block
and left the others as orphans.
- bulk_move_schematic_components shifted unit 1 and tore the part apart.
Add find_all_symbol_instance_blocks, returning every instance in file order.
find_symbol_instance_block now delegates to it and takes the first, so
single-placement callers are unchanged. field_value_range and
find_symbol_block become plural and the three handlers apply their edit to
each unit; batch_edit reports the unit count so the fan-out is visible.
Tests: five over a 3-unit part beside a single-unit part — field edit reaches
all three copies and spares the neighbour, single-unit still edits once,
missing field and unknown reference yield nothing, delete removes every unit
but keeps the lib_symbols definition, and the blocks are disjoint and ordered
as apply_edits requires. 439 -> 444.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Konnect could not draw a bus. SchematicBuilder already round-tripped bus and
bus_entry nodes and knew their slot in KiCad's required element order, so a
bus drawn in eeschema survived a Konnect edit, but nothing could create one.
Any repeated multi-signal link had to be one wire per signal or bare labels.
sch_wiring is at 19 tools and the registry invariant caps a toolset at 20, so
this lands as a new toolset rather than crowding that limit.
- add_bus / batch_add_bus: bus segments. Geometrically identical to wires;
KiCad distinguishes them by node name and the attached label.
- add_bus_entry: the 45-degree tick bridging wire and bus. Not decoration —
a wire and bus that merely touch are not connected without one.
- connect_pins_to_bus: per pin, the stub, the entry, AND the member label,
because bus membership is by name and a stub with no label joins nothing.
Pin resolution goes through the owning unit, so multi-unit parts work.
Also adds format_bus/format_bus_entry to konnect-sexp.
Verified against KiCad rather than only unit-tested: two 4-pin connectors
fanned onto one bus export a netlist with D0..D3 each carrying exactly its
two pins, ERC 0 violations. Unit tests cover the node name, the signed size
offset on an entry (wrong sign puts the tick past the bus, connecting
nothing), and registration. Registry counts and tool-directory.md updated:
18 -> 19 toolsets, 187 -> 191 tools. 447 -> 450.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two independent defects, either fatal at the file level:
- The node was inserted at content.rfind(')'), placing it after the symbol
instances and sheet_instances. KiCad 10 requires instances last.
sch_wiring::insert_before_close already existed for this, with the
ordering rule documented above it; this handler never used it.
- Only backslash and quote were escaped, so a multi-line annotation was
written with literal newline bytes inside the quoted string. KiCad's
reader wants the two-character escape.
Either one yields "Failed to load schematic" with no indication of which
element is at fault, while the tool reports success. Worse, once the file
will not load, kicad-cli erc leaves any previous report in place, so a stale
ERC result is what gets read next.
Make insert_before_close pub(crate) and use it; escape \r (dropped), \n and
\t alongside the existing handling.
Tests: text lands before the first symbol instance and after lib_symbols; a
multi-line string is written with \n escapes not raw bytes; quotes,
backslashes and tabs are escaped. The first two fail against the old code.
444 -> 447.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
batch_delete_no_connect existed with no batch add — an asymmetry that only shows up when it bites. Marking one MCU's unused pins is routinely 15-20 flags, each a separate add_no_connect round trip; this design needed 23 for the CH32V203, the programmer header and the three USB sockets. Mirrors the delete tool. A malformed entry is reported and skipped rather than aborting, so placing twenty flags fails as "nineteen landed and one is named" instead of losing the batch. Tests: all flags land in one pass, one bad entry does not cost the good ones, empty list is a no-op. sch_wiring 19 -> 20 tools (its cap), 192 -> 193 total. 455 -> 458. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nothing could set the sheet size. create_schematic emits a blank sheet with no (paper ...) node at all, and there was no way to change one, so a design that outgrew A4 either kept content hanging off the frame or forced a hand edit of the .kicad_sch — the thing the tooling exists to prevent. An undersized page is silent: content outside the frame still exports and still nets up, and ERC says nothing. The tool returns the chosen size in mm so a caller can check it against the layout extents. Handles A0-A5, A-E, US Letter/Legal/Ledger; replaces an existing paper node or inserts one into the header ahead of any element; an unknown size is a structured InvalidArgument that leaves the file untouched. Tests: replace without leaving the old size, insert into a blank sheet before the first element, portrait flag, unknown size writes nothing, and a table invariant that every entry is stored landscape since portrait swaps them. Registry and tool-directory updated: sch_components 18 -> 19, 191 -> 192 tools. 450 -> 455. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Editing a symbol in its library had no effect on schematics already using it. add_schematic_component and replace_component both go through ensure_lib_symbol_in_schematic, which short-circuits when a definition with that lib_id is already embedded, so the schematic keeps its stale copy. Hit for real: widening the TM16xx body in the library left every placed instance rendering at the old width, and replace_component with the same lib_id did not refresh it. eeschema has Tools > Update Symbols from Library for this; Konnect had nothing, leaving a manual GUI step as the only route. The new tool re-resolves each embedded lib_symbols entry from disk and replaces it, with an optional lib_id filter and dry_run. Wires and labels attach at pin coordinates, so a library edit that moved a pin would silently orphan them. The tool therefore compares pin anchors before and after and refuses any symbol whose pins moved, reporting why, unless allow_pin_moves is passed. Body size and pin length changes leave anchors untouched — the safe case, and the one that motivated this. sch_components 17 -> 18 tools; registry tool_count updated. 437 tests pass (4 added); clippy --workspace -D warnings clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Renaming a KiCad project by hand is a four-file rename plus an edit that is easy to miss: every symbol instance stores (project "name"), and the sheet instance path is keyed on it. Rename only the files and KiCad loads the design as unannotated — every reference designator is gone, and re-annotating renumbers parts that a PCB is already laid out against. rename_project renames the .kicad_pro/.kicad_sch/.kicad_pcb/.kicad_prl set and rewrites the internal references together, the equivalent of eeschema's File > Save As. dry_run reports the planned moves and rewrites without touching anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Changing Konnect's source and rebuilding does nothing until the MCP client is restarted, because the client spawns the server and holds it for the session. Replacing the binary underneath a running server just kills the connection. A stdio server cannot restart itself by exiting — the client owns the process lifecycle and does not respawn it mid-session. So reload_server execs into the binary on disk instead: that replaces the process image while keeping the PID and the inherited stdin/stdout pipes, so the connection is never broken and the client goes on talking to the new build. exec is a one-way door, so the new binary is run once (--version) and checked before the switch. A half-written copy, a failed link, or an unsigned binary macOS would kill becomes a refused call naming the reason rather than a server that is simply gone. confirm=true is required so a stray call cannot restart mid-task, and the reply is written before the switch since exec never returns on success. Windows gets a clear unsupported error — no exec equivalent keeps the pipes. Router state does not survive; the new image starts at the starter kit. That is self-healing: a call to a previously loaded tool returns toolset_not_loaded naming its toolset, so recovery is one hop. Also pins the meta-tool count in a test. It is quoted in DEV.md, README.md and tool-directory.md, so adding one now forces those to be updated in the same commit instead of drifting. Meta-tools 6 -> 7, docs updated to 194 total. 433 tests pass (3 added); clippy --workspace -D warnings clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
crates/schematic-viewer is its own workspace with its own Cargo.lock, and it depends on konnect-schematic-editor by path. Adding serde_json to that crate therefore invalidates the viewer's lockfile as well as the root one, and the viewer CI job runs with --locked, so it fails with "cannot update the lock file ... because --locked was passed". One added edge, no version churn. Verified with the command CI runs: cargo check --locked --manifest-path crates/schematic-viewer/Cargo.toml Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # crates/konnect-core/src/tools/sch_batch.rs
# Conflicts: # tool-directory.md
# Conflicts: # tool-directory.md
# Conflicts: # crates/konnect-core/src/tools/sch_components.rs # tool-directory.md
# Conflicts: # tool-directory.md
# Conflicts: # tool-directory.md
sch_components gains both set_schematic_page and update_symbols_from_library (17 -> 19) and sch_wiring gains batch_add_no_connect (19 -> 20), so the load_toolset batch assertion for [sch_components, sch_wiring] moves 36 -> 39. Registry counts, tool-directory rows and totals brought in line: 19 toolsets, 195 registered tools + 7 meta-tools. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
I guess this PR was created by accident? |
|
Thanks for putting this together, and for taking the trouble to replay the fork's work onto real upstream history rather than leaving it on a vendored branch — that part was the right call and it made this reviewable at all. I'm going to take the ten individual PRs (#170–#179) instead of this bundle, for a concrete reason rather than a policy one. I verified this branch is the exact union of the ten: every single-owner file diff is byte-identical to the corresponding PR, and the multi-owner files reconcile line-for-line. So nothing is missing. The problem is the history it would bring with it:
The ten separate PRs don't have any of these problems — they're each individually correct against main, which is exactly the "short PR series" shape CONTRIBUTING asks for. I'd rather merge them one at a time and keep a history that builds at every commit. I've reviewed all ten and will comment on each with its own verdict. Short version: #174 and #171 are clean merges, #179 is a genuine data-corruption fix I want, #170/#173/#175 need small changes, and #172/#176/#177/#178 need rework or are superseded — details on each PR. Closing this one in favour of the series. None of the work is lost. |
|
oups yes ! was meant to be local to my repo ! really sorry ! |
…DME/DEV Both are residue from merging the ten fix branches, and both were caught in upstream review of the bundle (mixelpixx#180): - sch_components.rs carried '#[cfg(test)]' twice over mod page_tests, left behind when the two branches' test modules were stitched together. Legal Rust, so it compiled and no test noticed. - the reconcile commit updated tool-directory.md to 19 toolsets / 195 tools + 7 meta but left DEV.md at '18 toolsets, 187 tools' and '6 meta-tools', and README.md at '187 tools across 18 on-demand toolsets' — the exact drift that commit existed to remove.
Brings this fork's
mainup to current upstream and makes it the VNS working version:mixelpixx/Konnect@5cd6454plus ten fixes, merged and gated together.Until now
mainhere was a byte-identical mirror of upstream, and the fork's actual worklived on
publish— a branch built from a vendored v0.2.0 tarball with no shared ancestrywith upstream. That made it useless as a base: a diff against upstream showed 208 files and
6060 deletions, because git could only read it as reverting everything upstream had done
since. Every fix here has now been replayed onto real upstream history instead.
Correctness fixes
Multi-unit symbols were treated as one placement. A multi-unit part is one
(symbol …)instance per unit, all sharing the reference, and four tools took the first match.
batch_connect_to_netresolved every pin against instance #1 and transformed it by thatunit's placement, so two different nets landed on one coordinate and were silently
shorted — no error, and the tool returned success. Found in real use:
X_CLK_INandX_DATA_INboth on U6 pin 1 of a 74HC14.batch_edit_schematic_componentswrote fields intounit 1 only; the delete tools left the other units as orphans;
bulk_movetore the partapart.
No user symbol library resolved.
resolve_lib_symbolscanned a hardcoded list of installdirectories for a file named after the library nickname and never read
sym-lib-table. On anynon-standard KiCad install — a macOS bundle outside
/Applications, a portable Windows copy —nothing resolved. Footprints got table-aware lookup in v0.2.1; symbols never did.
add_schematic_textwrote files KiCad could not open. It inserted the node after thesymbol instances (KiCad 10 requires those last) and wrote literal newlines where the format
wants
\n. Either alone makes the schematic fail to load with no indication of which elementis at fault, while the tool reports success. Worse, once the file will not load
kicad-cli sch ercleaves the previous report in place — so a stale ERC result is what gets read next.String escapes decoded wrongly.
unescapecollapsed backslashes last, soC:\\newcameback as
C:\plus a real newline. Hits Windows paths inDatasheet, 3D-model references andSheetfile.Added tooling
sch_bus—add_bus,batch_add_bus,add_bus_entry,connect_pins_to_bus.konnect-sexpalready hadformat_busandBusEntryDirectionandSchematicBuilderalready round-tripped bus nodes, but nothing could create one.
connect_pins_to_buswritesthe stub, the entry and the member label per pin, because bus membership in KiCad is by
name — a stub without a label joins nothing.
set_schematic_page— nothing could set the sheet size, and content off the frame stillexports and still nets up, so an undersized page is a silent defect.
update_symbols_from_library— eeschema's Update Symbols from Library. Refuses anysymbol whose pin anchors moved, since wires sit at the old coordinates.
rename_project— renames the four project files and the internal(project "name")references. Renaming files alone makes KiCad treat the design as unannotated, losing every
reference designator.
batch_add_no_connect—batch_delete_no_connectexisted with no batch add; one MCU'sunused pins is routinely 15–20 round trips.
reload_server—execs into the rebuilt binary in place, same PID and stdio pipes, sothe MCP session survives a rebuild. Verifies the new binary first.
Validation
cargo fmt --all -- --check,cargo clippy --workspace -- -D warnings, andcargo test --workspace --lib --tests— 515 passing.cargo check --locked --manifest-path crates/schematic-viewer/Cargo.tomlpasses too, which the viewer CI jobrequires.
Counts reconciled across the merge: 19 toolsets, 195 registered tools + 7 meta-tools.
sch_components17 → 19,sch_wiring19 → 20,project6 → 7, and thetools_addedassertion in
protocol_stdio.rs36 → 39.Three merge hazards were resolved rather than papered over: two pairs of branches appended
test modules at the same point in
sch_batch.rsandsch_components.rs, and every branchclaimed the same next
tool_count.Relationship to the upstream PRs
Each fix is also offered upstream on its own branch, one per PR (
up/*), each based directlyon
upstream/mainand carrying only its own change. This branch is the integrated result foruse here; those are the reviewable units. As they land upstream, rebuild this branch on the
new
upstream/mainrather than merging it back.Not included, and still fork-only:
publish-to-fork.sh,version_history.md,CLAUDE.md,docs/CODE_REVIEW.mdand the KiCad bug reports.