Skip to content

fix(schematic): snap add_power_symbol to the schematic grid - #665

Merged
neusse merged 1 commit into
mainfrom
fix/662-power-symbol-grid
Sep 21, 2026
Merged

neusse merged 1 commit into
mainfrom
fix/662-power-symbol-grid

Conversation

@mixelpixx

@mixelpixx mixelpixx commented Sep 20, 2026

Copy link
Copy Markdown
Owner

Summary

add_power_symbol wrote the requested position as given, while add_schematic_component and batch_place_components snap to KiCad's 1.27 mm schematic grid. Wires and labels are snapped too, so a power symbol left off the grid could not be reached by them. On a real 8-sheet design that was 21 endpoint_off_grid warnings and a set of pin_not_connected errors. It now snaps like the other two placers.

Closes #662

Kept separate from #583 (#PWR001 versus eeschema's #PWR01), which is designator spelling in the same handler and needs its own migration note.

Approach

Reproduced first through the release binary on main's behaviour, with kicad-cli 10.0.5's ERC as the oracle (table below): the resistor lands on the grid, the three power symbols do not, and ERC reports endpoint_off_grid: 3.

Fix. One line in handle_add_power_symbol: snap_point(x, y, 1.27), the shared helper place_one_component already uses, applied before anything else reads the point. The bound placement intent the readback is checked against, the field anchors and the instance record therefore all agree with what is written. x and y in the response were already read back from the committed file through placed_component_readback, so they report the snapped position without any response change.

Why the suite missed it. The shared placement acceptance test hands all three placers an off-grid point, but its snap assertion sat behind symbol.reference() == Some("U999"). A power symbol is auto-numbered #PWRnnn, so the one placer that did not snap was the one placer the check skipped. The check now runs for every placed symbol, on both axes, and asserts the coordinate is on the grid rather than merely different from the request.

Scope note. A request already on the grid, which includes any pin endpoint of a placed component, lands exactly where it did before. A pin endpoint that is itself off the 1.27 mm grid cannot be reached by any Konnect placer or wire today, because they all snap; that is not changed here.

Architectural fit: reuses the shared snap helper and the shared placement readback; it removes a divergence between the three placers and adds nothing new.

Branch and dependencies

Base branch: main at cdcafa8 (rebuilt once after #663 merged, as the train asks). The rebuilt commit f1db987 carries per-file changes byte-identical to the reviewed a66f322; only its position in docs/API_MIGRATIONS.md moved, below #663's entry.
Depends on: nothing. Series order: none. Unique commits/acceptance criteria owned by this PR: the whole of #662. Next PR to promote after this one: none. Overlap: the top of docs/API_MIGRATIONS.md and one tool-directory.md row (as #663 and #581's PR); whichever lands later reconstructs once. #666 (#583, designator spelling) touches the same handler lines apart and follows this PR in the train; a trial merge of the two passes both test modules.

Compatibility and safety

  • Patch per GOVERNANCE.md: a fix, no argument or response field added or removed. Recorded in docs/API_MIGRATIONS.md because the coordinates written for an off-grid request change, by at most 0.635 mm on each axis.
  • File mutation path unchanged: the same single write, the same stale-instance refusals, the same committed-file readback.
  • Bundled kicad-schematic skill gains two lines saying the position is snapped and reported.

Validation

Changed tool behavior

Behavior Contract and evidence for this change
Accepted inputs and declared defaults Unchanged (schematic, power_net, x, y, rotation default 0).
Invalid/unsupported inputs and structured errors Unchanged.
Target, data source and prerequisite state Unchanged: the saved .kicad_sch, with the existing sheet-instance validation before writing.
Observed changes and preserved unrelated objects The new symbol is written at the snapped point; the acceptance test still asserts every existing symbol is byte-identical and the root and project files are untouched.
Failure before/after mutation, including applied work Unchanged.
Recovery from partial/uncertain results without repeating applied work Unchanged; the response reports the placed coordinates from the committed file, so a caller wires to where the symbol is, not to where it asked.

Commands run on f1db987 (Windows 11, kicad-cli 10.0.5), exit codes captured directly:

  • cargo fmt --all -- --check
  • cargo clippy --workspace --locked --all-targets -- -D warnings
  • cargo test --workspace --locked --lib --tests2018 passed / 0 failed
  • cargo test --workspace --locked --doc
  • cargo xtask fix-doc-counts --check — unchanged
  • cargo test -p konnect-core reliability_contract — green
  • Real-KiCad checks: kicad-cli 10.0.5 ERC before and after, below.

Tests. native_placement_preserves_unique_and_reused_paths_with_committed_readback now asserts, for every symbol placed by each of the three placers on the KiCad-authored complex_hierarchy fixture, that both axes are on the 1.27 mm grid. Two existing field-anchor tests in sch_wiring::power_symbol_tests requested (100.0, 80.0), which is off the grid, and asserted the library's anchor offsets from that point; the full gate caught them, and they now request an on-grid point so the offsets they pin are not mixed with the snap. New served regression a_power_symbol_is_snapped_through_the_served_dispatch: GND requested at (100.1, 80.2) through McpHandler::handle_message reads (100.33, 80.01) from the response and from the committed file.

Neuters (each guard reverted alone, --no-fail-fast):

Guard reverted Caught by
the snap is removed (main's behaviour) native_placement_preserves_unique_and_reused_paths_with_committed_readback (the widened assertion: add_power_symbol: x = 100.1 is off the 1.27 mm grid) and the served a_power_symbol_is_snapped_through_the_served_dispatch
only x is snapped the same two tests, on the y axis

Both neuters compiled; neither was caught by a build break. The first row is the "widened assertion failing before the fix" the issue asks for: with the old guard behind U999, that neuter passed the acceptance test.

E2E through the release binary over stdio, kicad-cli 10.0.5 ERC as the oracle (e2e662.py: create_project, one Device:R requested at (100.0, 100.0), three GND symbols requested off grid):

call main this head
add_schematic_component Device:R requested at (100.0, 100.0) placed at (100.33, 100.33) placed at (100.33, 100.33)
add_power_symbol GND requested at (100.1, 120.2) reported and written at (100.1, 120.2) reported and written at (100.33, 120.65)
add_power_symbol GND requested at (130.4, 120.2) (130.4, 120.2) (130.81, 120.65)
add_power_symbol GND requested at (150.7, 90.3) (150.7, 90.3) (151.13, 90.17)
kicad-cli sch erc --severity-all on the saved file endpoint_off_grid: 3, pin_not_connected: 5, power_pin_not_driven: 1 endpoint_off_grid: 0, pin_not_connected: 5, power_pin_not_driven: 1

The reported coordinates are the ones in the file in both columns (the response is a readback); only where the symbol lands changed. The remaining two ERC kinds are the same before and after because the driver wires nothing: it isolates the grid defect.

Review checklist

  • The diff is focused and contains no generated output, personal data, or unrelated cleanup.
  • The branch includes current upstream/main, has no merge conflicts, and CI passed on this exact head.
  • The branch was based on latest upstream/main, not a release tag.
  • The PR shows only its unique commits and diff; dependencies and series position are explicit.
  • Every review conversation is resolved; any post-review push or base change has been reviewed again on the new exact head.
  • No new public names.
  • New behavior has regression coverage, and the guard that skipped it is widened.
  • File mutations are atomic and preserve unrelated content (unchanged path).
  • IPC mutations: none.
  • No tools added/removed; counts unchanged.

Maintainer merge state

  • The PR has exactly one current status:* workflow label.
  • status:ready-to-merge applies to this exact head SHA.
  • All required checks and review conversations satisfy the main ruleset.
  • Merge with gh pr merge N --merge after final verification.
  • Terminal issue closure: Closes #662. Next PR to promote: none.

🤖 Generated with Claude Code

@mixelpixx mixelpixx added bug Something isn't working P1 High-value workflow reliability area:schematic Schematic capture and analysis labels Sep 20, 2026
@mixelpixx
mixelpixx marked this pull request as ready for review September 20, 2026 14:25
@mixelpixx
mixelpixx requested a review from neusse as a code owner September 20, 2026 14:25
@mixelpixx mixelpixx added the status:waiting-on-review Next actor: maintainer label Sep 20, 2026
@neusse neusse added status:waiting-on-dependency Next actor: the dependency owner — see linked blocking issue and removed status:waiting-on-review Next actor: maintainer labels Sep 20, 2026
@neusse

neusse commented Sep 20, 2026

Copy link
Copy Markdown
Collaborator

Queue position: next after #663. I reviewed the unique change on exact head a66f322a7bbea34aa4cc26607b0cc68dde45a49e: the shared snap helper is applied before placement intent, saved and reported coordinates are read back, the three-placer acceptance assertion is widened, the served regression and real-KiCad ERC evidence are appropriate, and all ten required checks are green.

Do not reconstruct yet. #663 is the current base-forming PR and overlaps this PR in docs/API_MIGRATIONS.md / tool-directory.md. After #663 merges, rebuild this branch once from current main with only its unique commit, rerun CI, and request final exact-head review. The code intent is accepted; the changed head will still require the normal final verification.

add_schematic_component and batch_place_components snap the requested
position to KiCad's 1.27 mm grid through place_one_component;
add_power_symbol passed its coordinates straight to the symbol (#662).
Wires and labels are snapped as well, so a power symbol left off the
grid could not be reached by them. On a real 8-sheet design that was 21
endpoint_off_grid warnings and a set of pin_not_connected errors.

The handler now snaps before anything else uses the point, so the bound
placement intent, the field anchors and the instance record all agree
with what is written. The response already reported x and y from the
committed file; they now show the snapped position.

The shared placement acceptance test nearly caught this: it hands all
three placers an off-grid point, but its snap assertion sat behind the
U999 reference, and a power symbol is auto-numbered #PWRnnn, so the one
placer that did not snap was the one placer the check skipped. The check
now runs for every placed symbol and asserts both axes are on the grid.
A served-dispatch regression places GND at (100.1, 80.2) on the
KiCad-authored hierarchy fixture and reads (100.33, 80.01) from both the
response and the committed file.

Two field-anchor tests requested (100.0, 80.0), which is off the grid,
and asserted the library's anchor offsets from that point. They now
request an on-grid point, so the offsets they pin are not mixed with the
snap.

Closes #662

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@neusse neusse added status:waiting-on-author Next actor: the PR author — one checklist, 14-day target and removed status:waiting-on-dependency Next actor: the dependency owner — see linked blocking issue labels Sep 20, 2026
@neusse

neusse commented Sep 20, 2026

Copy link
Copy Markdown
Collaborator

#663 has merged; please reconstruct this PR now. Current main is cdcafa8. Rebuild fix/662-power-symbol-grid from that commit with only the unique #662 change, resolve the shared docs/API_MIGRATIONS.md / tool-directory.md edits, push with --force-with-lease, and let all ten required checks rerun. The current head a66f322a is now conflicting and its previous green checks are stale. Reply when the new head is ready for exact-head review.

This remains the next PR in the merge train; #666 and #664 stay behind it.

@mixelpixx
mixelpixx force-pushed the fix/662-power-symbol-grid branch from a66f322 to f1db987 Compare September 20, 2026 17:23
@mixelpixx

Copy link
Copy Markdown
Owner Author

#663 merged as cdcafa8 (your approved head 08155b2; the merge tree is identical to it, #580 closed, acceptance posted there). This branch is rebuilt once onto it, as you asked.

New exact head: f1db987, one commit on cdcafa8.

  • Same change. Added and removed lines compared file by file against the reviewed a66f322: sch_wiring.rs, schematic_placement_tests.rs, the kicad-schematic skill and tool-directory.md are byte-identical, and so are the added lines of the migration entry. The only conflict was the top of docs/API_MIGRATIONS.md; the entry now sits directly below fix(config): refuse a config file that cannot be used #663's. No conflict markers in the tree.
  • Gate on f1db987, exit codes captured directly: fmt, clippy -D warnings, --lib --tests 2018 passed / 0 failed (fix(config): refuse a config file that cannot be used #663's 2017 plus this PR's served regression), --doc, fix-doc-counts --check unchanged, reliability_contract green.
  • CI 10/10 on f1db987; merge state CLEAN.
  • E2E re-run on the release binary built from f1db987: the three off-grid GND requests land on the grid, and kicad-cli 10.0.5 ERC reports endpoint_off_grid: 0 (was 3 on main's behaviour).

Body updated with the new base, head and counts. Label moved to status:waiting-on-review for your final exact-head check. #666 is rebuilt after this lands, then #664.

One addition to the queue since your sweep: #668 (Closes #657, the named sync diagnostics). It overlaps the train only at the top of docs/API_MIGRATIONS.md. I am holding its reconstruction until you say where it sits.

@mixelpixx mixelpixx added status:waiting-on-review Next actor: maintainer and removed status:waiting-on-author Next actor: the PR author — one checklist, 14-day target labels Sep 20, 2026

@neusse neusse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head f1db987e75a3e838540ce44ebdfcc8592840d374 against current main cdcafa8ceebc8cec28a110b4cac838c0bc407e81.

The reconstruction contains the same focused #662 change previously reviewed: snapping occurs before placement-derived state, committed-file readback remains authoritative, the widened all-placer/two-axis assertion closes the prior coverage hole, and the served regression plus real KiCad ERC evidence match the issue. Closes #662 is correct. All ten required checks are green and there are no unresolved review threads.

@neusse neusse added status:ready-to-merge Next actor: automation or maintainer — exact head reviewed and removed status:waiting-on-review Next actor: maintainer labels Sep 21, 2026
@neusse
neusse merged commit 87e3e8f into main Sep 21, 2026
12 checks passed
@neusse
neusse deleted the fix/662-power-symbol-grid branch September 21, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:schematic Schematic capture and analysis bug Something isn't working P1 High-value workflow reliability status:ready-to-merge Next actor: automation or maintainer — exact head reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(schematic): add_power_symbol does not snap to the 1.27 mm grid though every other placer does

2 participants