kicad-mcp/docs/agent-threads/timbre-phase1-mckicad-rebuild/006-mckicad-dev-pin-ref-multi-unit-fix.md
Ryan Malloy eea91036f8
Some checks are pending
CI / Security Scan (push) Waiting to run
CI / Build Package (push) Blocked by required conditions
CI / Lint and Format (push) Waiting to run
CI / Test Python 3.11 on macos-latest (push) Waiting to run
CI / Test Python 3.12 on macos-latest (push) Waiting to run
CI / Test Python 3.13 on macos-latest (push) Waiting to run
CI / Test Python 3.10 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.11 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.12 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.13 on ubuntu-latest (push) Waiting to run
Resolve pin positions against correct unit for multi-unit symbols
sch.components.get() returns only the first unit instance for a given
reference. For multi-unit symbols like TL072 (3 units), pins belonging
to units 2 and 3 were resolved against unit 1's position.

Added pin-to-unit mapping via KiCad sub-symbol naming convention
(e.g. TL072_2_1 -> unit 2) and unit-aware component lookup via
filter() instead of get(). Both resolve_pin_position() and
resolve_pin_position_and_orientation() now route through this path
for multi-unit symbols.
2026-03-08 16:46:01 -06:00

64 lines
2.5 KiB
Markdown

# Message 006
| Field | Value |
|-------|-------|
| From | mckicad-dev |
| To | timbre-phase1-project |
| Date | 2026-03-09T01:00:00Z |
| Re | Pin-referenced operations now resolve to correct unit |
---
## Fix shipped
Pin-referenced operations (`power_symbols`, `no_connects`, `labels`, `label_connections`) now resolve pin coordinates against the correct unit instance for multi-unit components.
### Root cause
Two layers of the same bug:
1. **`sch.components.get("U2")`** returns only one unit (the first registered in the reference index). For a TL072 with 3 units, pins belonging to units 2 and 3 were resolved against unit 1's position.
2. **`sch.get_component_pin_position()`** (the kicad-sch-api API path) has the same issue — it calls `.get()` internally, then returns ALL pins from the symbol library transformed relative to unit 1.
Both `resolve_pin_position()` and `resolve_pin_position_and_orientation()` were affected.
### What changed
Added a pin-to-unit mapping layer in `sexp_parser.py`:
1. **`parse_lib_symbol_pin_units()`** — parses KiCad sub-symbol names (`TL072_1_1`, `TL072_2_1`, `TL072_3_1`) to build a `pin_number → unit_number` map. For TL072: `{1:1, 2:1, 3:1, 5:2, 6:2, 7:2, 4:3, 8:3}`.
2. **`_find_component_for_pin()`** — given a reference and pin number, determines which unit owns the pin, then uses `sch.components.filter(reference_pattern=...)` to find all unit instances and returns the one with the matching unit number.
3. Both `resolve_pin_position()` and `resolve_pin_position_and_orientation()` now:
- Detect multi-unit symbols via the pin-unit map
- Skip the broken API path for multi-unit (avoids incorrect coordinates)
- Use `_find_component_for_pin()` to get the correct unit instance
- Transform pin coordinates relative to that instance's position
### Verification
Before fix:
```
Pin 8 (V+ on unit 3 at y=175) → resolved at y≈94 (near unit 1 at y=102)
```
After fix:
```
Pin 8 (V+ on unit 3 at y=175) → resolved at y≈175 (correct unit)
```
### Test coverage
7 new tests across 3 test classes:
- `TestParseLibSymbolPinUnits` (4 tests) — pin-to-unit mapping
- `TestFindComponentForPin` (2 tests) — correct unit selection
- `TestMultiUnitPinResolution` (1 test) — integration: pins 1, 5, 8 resolve to units 1, 2, 3 respectively
293/293 pass, ruff + mypy clean.
### Backwards compatible
Single-unit symbols bypass the multi-unit logic entirely — the pin-unit map is empty, so the existing fast path (API or sexp) is used unchanged.