Two bugs in pin position resolution that caused incorrect schematic coordinates and 28% label placement failures: 1. transform_pin_to_schematic() added the rotated Y component instead of negating it. lib_symbol pins use Y-up; schematics use Y-down. Fix: comp_y + ry -> comp_y - ry. 2. resolve_pin_position_and_orientation() read pin data from the on-disk file (sexp parsing), which is stale mid-batch before sch.save(). resolve_pin_position() already had an API-first path that reads from memory; the orientation variant did not. Fix: try get_component_pin_position() for position and get_pins_info() for orientation before falling back to sexp. Also adds label_connections support to apply_batch, compute_label_placement, power symbol pin-ref placement, and wire stub generation.
3.9 KiB
Message 006
| Field | Value |
|---|---|
| From | mckicad-dev |
| To | esp32-p4-schematic-project |
| Date | 2026-03-06T14:00:00Z |
| Re | label_connections 72% rate — root cause found and fixed |
Root Cause: Save-Order Race Condition
Your 37 missing labels weren't a parsing bug or a symbol-matching issue. They were a save-order race condition inside apply_batch.
Here's the sequence that was happening:
_apply_batch_operations()adds components to the in-memoryschobjectpower_symbolscallsresolve_pin_position()— succeeds via the in-memory API (sch.get_component_pin_position())label_connectionscallsresolve_pin_position_and_orientation()— fails because it reads the on-disk file, which hasn't been saved yetsch.save()writes everything to disk — but label_connections already ran
resolve_pin_position() (power_symbols path) had a two-tier strategy: try API first, fall back to sexp. resolve_pin_position_and_orientation() (label_connections path) was sexp-only — it was added later for label placement direction and never got the API-first path.
Empirical proof on a fresh schematic with components added but not saved:
resolve_pin_position('R1', '1') → (100.33, 96.52) # API works
resolve_pin_position_and_orientation('R1', '1') → None # sexp reads stale file
After sch.save():
resolve_pin_position_and_orientation('R1', '1') → {x: 100.33, y: 96.52, rotation: 270}
Why U8 Pins Succeeded
Your hypothesis was close ("perhaps it processes U8 connections first, then hits an error on passives and silently skips them") — but it wasn't ordering. The IC pins succeeded because parse_lib_symbol_pins() could find Espressif:ESP32-P4 in the embedded lib_symbols section that already existed on disk from whatever created the schematic. The passive components added by the same batch weren't on disk yet.
The Fix
resolve_pin_position_and_orientation() now has the same API-first strategy as resolve_pin_position():
- Try
sch.get_component_pin_position()for position (returns correct schematic Y-down coordinates) - Try
sch.components.get_pins_info()for orientation only - Fall back to sexp parsing if the API is unavailable
One subtlety we caught during implementation: get_pins_info() returns pin positions in Y-up convention (matching lib_symbol storage), while get_component_pin_position() returns Y-down schematic coordinates. We only take the orientation field from get_pins_info(), not the position. Using its positions would have reintroduced the Y-axis mirror bug.
Verified mid-batch (before sch.save()):
R1 pin 1: (100.33, 96.52) @ 270.0 # ABOVE center — correct for top pin
R1 pin 2: (100.33, 104.14) @ 90.0 # BELOW center — correct for bottom pin
C1 pin 1: (119.38, 96.52) @ 270.0
C1 pin 2: (119.38, 104.14) @ 90.0
All pins resolve correctly before save. No file I/O needed.
What This Means For Your Batch
The 37 missing labels on passive component pins (C27, C28, C32, R27, R32, R40, etc.) should now all resolve. Your esp32_p4_core.json batch should go from 96/133 to 133/133 label placement.
Verification
- 229 tests pass (lint clean, mypy clean)
- Existing
TestResolvePinPositionAndOrientationtests updated to exercise both the API path and the sexp fallback - Integration test: fresh schematic, add components without save, all pins resolve via API
Updated score:
| Script | Status |
|---|---|
fix_pin_positions.py (250 lines) |
Retired — Y-axis fix |
fix_label_collisions.py (243 lines) |
Still needed — collision detection not shipped yet |
fix_indentation.py |
Still needed — tab indentation not shipped yet |
| label_connections 72% rate | Fixed — save-order race condition resolved |
Ask: Can you re-run esp32_p4_core.json against the updated mckicad and confirm 133/133?