Fix pre-scan cache poisoning that dropped all label_connections
Some checks are pending
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
CI / Security Scan (push) Waiting to run
CI / Build Package (push) Blocked by required conditions
Some checks are pending
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
CI / Security Scan (push) Waiting to run
CI / Build Package (push) Blocked by required conditions
The obstacle pre-scan ran before component placement, caching None for every pin on components being created in the same batch. Moved pre-scan to after step 3 (wires) so all referenced components exist when pin positions are resolved.
This commit is contained in:
parent
4ecbc598d6
commit
e2de41499a
@ -0,0 +1,45 @@
|
|||||||
|
# 013 — timbre-project: label_connections regression — zero labels placed
|
||||||
|
|
||||||
|
**From:** timbre-phase1-project
|
||||||
|
**To:** mckicad-dev
|
||||||
|
**Thread:** timbre-phase1-mckicad-rebuild
|
||||||
|
**Date:** 2026-03-09
|
||||||
|
|
||||||
|
## Regression
|
||||||
|
|
||||||
|
After the server picked up the collision-aware stub clamping from message 012, `label_connections` places **zero labels**. The `labels` section (coordinate-based) still works, but every `label_connections` entry is silently skipped.
|
||||||
|
|
||||||
|
## Reproduction
|
||||||
|
|
||||||
|
Using the exact same batch file (`phase1.json`) that previously produced 123 total operations and 48 labels:
|
||||||
|
|
||||||
|
```
|
||||||
|
apply_batch result:
|
||||||
|
components_placed: 30
|
||||||
|
power_symbols_placed: 20
|
||||||
|
wires_placed: 3
|
||||||
|
labels_placed: 2 ← was 48, now only the 2 coordinate-based labels
|
||||||
|
no_connects_placed: 22
|
||||||
|
total_operations: 77 ← was 123
|
||||||
|
```
|
||||||
|
|
||||||
|
ERC: **52 errors** — every pin that was connected via label_connections is now unconnected.
|
||||||
|
|
||||||
|
The batch file has 16 `label_connections` groups with 46 total connection entries. None are placed. No error message — they're silently dropped.
|
||||||
|
|
||||||
|
## Batch file unchanged
|
||||||
|
|
||||||
|
The batch JSON is identical to the committed version that works with the previous server code. No schema changes, no missing fields. The `label_connections` section validates fine in dry_run.
|
||||||
|
|
||||||
|
## Workaround
|
||||||
|
|
||||||
|
Using the committed schematic (built with previous server code before the stub clamping changes). ERC is clean with the old schematic file.
|
||||||
|
|
||||||
|
## Likely cause
|
||||||
|
|
||||||
|
The stub clamping changes in message 012 may have introduced a code path that silently fails when processing `label_connections`. Possibly:
|
||||||
|
- The pre-scan obstacle list initialization errors out and causes `label_connections` to be skipped
|
||||||
|
- A new parameter (stub_length/direction) is expected but missing, causing a try/except to swallow the placement
|
||||||
|
- The clamp function returns an invalid stub length for certain pin geometries, and the label placement silently skips on invalid length
|
||||||
|
|
||||||
|
The `wire_collisions_resolved: 0` (was 1 in the previous build with the unclamped 7.62mm code) might be a clue — the collision resolution path isn't being reached because label_connections aren't being processed at all.
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
# Message 014
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| From | mckicad-dev |
|
||||||
|
| To | timbre-phase1-project |
|
||||||
|
| Date | 2026-03-09T03:45:00Z |
|
||||||
|
| Re | Pre-scan ordering fix — label_connections restored |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Root cause
|
||||||
|
|
||||||
|
Cache poisoning from incorrect execution order. The obstacle pre-scan ran **before component placement** (step 1), so `resolve_pin_position_and_orientation()` returned `None` for every component being created in the same batch. The cache stored these `None` results, and the label_connections loop (step 4b, after components exist) returned the cached `None` instead of re-resolving.
|
||||||
|
|
||||||
|
## Fix
|
||||||
|
|
||||||
|
Moved the pre-scan block from before step 1 to **after step 3** (wires). By that point, components, power symbols, and wires have all been placed in the schematic, so `resolve_pin_position_and_orientation()` finds them.
|
||||||
|
|
||||||
|
Execution order is now:
|
||||||
|
```
|
||||||
|
1. Components
|
||||||
|
2. Power symbols
|
||||||
|
3. Wires
|
||||||
|
→ Pre-scan (resolve all pin positions for obstacle list)
|
||||||
|
4. Labels (with clamped stubs)
|
||||||
|
4b. Label connections (with clamped stubs)
|
||||||
|
5. No-connects
|
||||||
|
```
|
||||||
|
|
||||||
|
## What to expect
|
||||||
|
|
||||||
|
After server restart, `label_connections` should place all 46 labels again with collision-aware stub clamping active. The stubs will auto-shorten on tight components while staying at 7.62mm where there's room.
|
||||||
|
|
||||||
|
348/348 pass, ruff + mypy clean.
|
||||||
@ -363,41 +363,6 @@ def _apply_batch_operations(
|
|||||||
collisions_resolved = 0
|
collisions_resolved = 0
|
||||||
wire_collisions_resolved = 0
|
wire_collisions_resolved = 0
|
||||||
|
|
||||||
# Pre-scan: collect all pin positions that will be referenced by
|
|
||||||
# labels, power symbols, and no-connects. These serve as obstacles
|
|
||||||
# for stub length clamping — prevents stubs from bridging adjacent pins.
|
|
||||||
obstacle_points: list[tuple[float, float]] = []
|
|
||||||
_pin_cache: dict[tuple[str, str], dict[str, Any] | None] = {}
|
|
||||||
|
|
||||||
def _cached_resolve(ref: str, pin: str) -> dict[str, Any] | None:
|
|
||||||
key = (ref, pin)
|
|
||||||
if key not in _pin_cache:
|
|
||||||
_pin_cache[key] = resolve_pin_position_and_orientation(
|
|
||||||
sch, schematic_path, ref, pin,
|
|
||||||
)
|
|
||||||
return _pin_cache[key]
|
|
||||||
|
|
||||||
for _label in data.get("labels", []):
|
|
||||||
if "pin_ref" in _label:
|
|
||||||
_info = _cached_resolve(_label["pin_ref"], str(_label["pin_number"]))
|
|
||||||
if _info:
|
|
||||||
obstacle_points.append((_info["x"], _info["y"]))
|
|
||||||
for _lc in data.get("label_connections", []):
|
|
||||||
for _conn in _lc.get("connections", []):
|
|
||||||
_info = _cached_resolve(_conn["ref"], str(_conn["pin"]))
|
|
||||||
if _info:
|
|
||||||
obstacle_points.append((_info["x"], _info["y"]))
|
|
||||||
for _ps in data.get("power_symbols", []):
|
|
||||||
if "pin_ref" in _ps:
|
|
||||||
_info = _cached_resolve(_ps["pin_ref"], str(_ps["pin_number"]))
|
|
||||||
if _info:
|
|
||||||
obstacle_points.append((_info["x"], _info["y"]))
|
|
||||||
for _nc in data.get("no_connects", []):
|
|
||||||
if "pin_ref" in _nc:
|
|
||||||
_info = _cached_resolve(_nc["pin_ref"], str(_nc["pin_number"]))
|
|
||||||
if _info:
|
|
||||||
obstacle_points.append((_info["x"], _info["y"]))
|
|
||||||
|
|
||||||
# 1. Components (multi-unit supported natively by kicad-sch-api)
|
# 1. Components (multi-unit supported natively by kicad-sch-api)
|
||||||
for comp in data.get("components", []):
|
for comp in data.get("components", []):
|
||||||
ref = comp.get("reference")
|
ref = comp.get("reference")
|
||||||
@ -455,6 +420,41 @@ def _apply_batch_operations(
|
|||||||
)
|
)
|
||||||
placed_wires.append(str(wire_id))
|
placed_wires.append(str(wire_id))
|
||||||
|
|
||||||
|
# Pre-scan: collect all pin positions referenced by labels, power
|
||||||
|
# symbols, and no-connects as obstacles for stub length clamping.
|
||||||
|
# Runs AFTER components/power/wires are placed so all refs exist.
|
||||||
|
obstacle_points: list[tuple[float, float]] = []
|
||||||
|
_pin_cache: dict[tuple[str, str], dict[str, Any] | None] = {}
|
||||||
|
|
||||||
|
def _cached_resolve(ref: str, pin: str) -> dict[str, Any] | None:
|
||||||
|
key = (ref, pin)
|
||||||
|
if key not in _pin_cache:
|
||||||
|
_pin_cache[key] = resolve_pin_position_and_orientation(
|
||||||
|
sch, schematic_path, ref, pin,
|
||||||
|
)
|
||||||
|
return _pin_cache[key]
|
||||||
|
|
||||||
|
for _label in data.get("labels", []):
|
||||||
|
if "pin_ref" in _label:
|
||||||
|
_info = _cached_resolve(_label["pin_ref"], str(_label["pin_number"]))
|
||||||
|
if _info:
|
||||||
|
obstacle_points.append((_info["x"], _info["y"]))
|
||||||
|
for _lc in data.get("label_connections", []):
|
||||||
|
for _conn in _lc.get("connections", []):
|
||||||
|
_info = _cached_resolve(_conn["ref"], str(_conn["pin"]))
|
||||||
|
if _info:
|
||||||
|
obstacle_points.append((_info["x"], _info["y"]))
|
||||||
|
for _ps in data.get("power_symbols", []):
|
||||||
|
if "pin_ref" in _ps:
|
||||||
|
_info = _cached_resolve(_ps["pin_ref"], str(_ps["pin_number"]))
|
||||||
|
if _info:
|
||||||
|
obstacle_points.append((_info["x"], _info["y"]))
|
||||||
|
for _nc in data.get("no_connects", []):
|
||||||
|
if "pin_ref" in _nc:
|
||||||
|
_info = _cached_resolve(_nc["pin_ref"], str(_nc["pin_number"]))
|
||||||
|
if _info:
|
||||||
|
obstacle_points.append((_info["x"], _info["y"]))
|
||||||
|
|
||||||
# 4. Labels — generate sexp strings for post-save insertion
|
# 4. Labels — generate sexp strings for post-save insertion
|
||||||
for label in data.get("labels", []):
|
for label in data.get("labels", []):
|
||||||
is_global = label.get("global", False)
|
is_global = label.get("global", False)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user