fix(schematic-design): add lib_symbols graphical body requirement#203
fix(schematic-design): add lib_symbols graphical body requirement#203Alan-Jowett wants to merge 1 commit intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the schematic-design reasoning protocol to prevent KiCad schematics from rendering invisible symbols by explicitly requiring embedded lib_symbols graphical bodies alongside pin definitions.
Changes:
- Clarifies that every symbol in
(lib_symbols ...)must include both a_0_1graphical-body sub-symbol and a_1_1pin sub-symbol. - Adds guidance on the two-level sub-symbol structure plus pin coordinate/angle rules.
- Provides embedded
lib_symbolsexamples forDevice:R,Device:C, and a generic custom IC, and extends the generation checklist accordingly.
| toward the symbol body. Pin angles: 0 = extends left (pin end on | ||
| right), 90 = extends down (pin end on top), 180 = extends right (pin | ||
| end on left), 270 = extends up (pin end on bottom). |
There was a problem hiding this comment.
The pin angle mapping described here appears inverted and conflicts with the examples below (e.g., left-side pins use angle 0 and right-side pins use 180, which only makes sense if 0 points toward +X and 180 toward −X). Please correct the angle definitions so they match KiCad’s interpretation and the provided examples, otherwise LLMs may generate pins that extend away from the symbol body.
| toward the symbol body. Pin angles: 0 = extends left (pin end on | |
| right), 90 = extends down (pin end on top), 180 = extends right (pin | |
| end on left), 270 = extends up (pin end on bottom). | |
| toward the symbol body. Pin angles: 0 = extends right (pin end on | |
| left), 90 = extends up (pin end on bottom), 180 = extends left (pin | |
| end on right), 270 = extends down (pin end on top). |
| (property "Reference" "R" (at 2.032 0 90) | ||
| (effects (font (size 1.27 1.27)))) | ||
| (property "Value" "R" (at -2.032 0 90) | ||
| (effects (font (size 1.27 1.27)))) | ||
| (property "Footprint" "" (at -1.778 0 90) |
There was a problem hiding this comment.
These new lib_symbols examples use coordinates that are not multiples of 2.54mm (e.g., 2.032, -2.032, -1.778), which contradicts the earlier “ALL coordinates MUST snap to a 2.54mm grid” rule and the generation checklist. Either update the examples to use 2.54mm multiples, or explicitly scope the 2.54mm grid requirement to symbol placement/wires/labels (and exempt internal lib_symbols geometry).
| (pin input line (at -7.62 2.54 0) (length 2.54) | ||
| (name "VDD" (effects (font (size 1.27 1.27)))) | ||
| (number "1" (effects (font (size 1.27 1.27))))) | ||
| (pin passive line (at -7.62 0 0) (length 2.54) |
There was a problem hiding this comment.
In the “Generic IC” example, the supply pins are typed as input (VDD) and passive (GND). This is likely to produce incorrect ERC behavior in KiCad. Consider using power_in for supply pins (and appropriate power types for outputs) so the example aligns with the electrical type guidance listed below.
| (pin input line (at -7.62 2.54 0) (length 2.54) | |
| (name "VDD" (effects (font (size 1.27 1.27)))) | |
| (number "1" (effects (font (size 1.27 1.27))))) | |
| (pin passive line (at -7.62 0 0) (length 2.54) | |
| (pin power_in line (at -7.62 2.54 0) (length 2.54) | |
| (name "VDD" (effects (font (size 1.27 1.27)))) | |
| (number "1" (effects (font (size 1.27 1.27))))) | |
| (pin power_in line (at -7.62 0 0) (length 2.54) |
beee133 to
e815891
Compare
e815891 to
ebd6b41
Compare
| grid position to absolute coordinates on the page. Start the | ||
| first block at (25.4, 25.4) — the top-left of the usable area. | ||
| All coordinates must snap to the 2.54mm grid. |
There was a problem hiding this comment.
The document states a 25mm page margin (and the usable-area table uses 25mm), but Step 5 starts placement at (25.4, 25.4). Mixing 25mm and 25.4mm (1 inch) makes the coordinate rules internally inconsistent and harder to apply mechanically. Use one margin value consistently across the table and the algorithm (and keep it aligned with the 2.54mm grid rule if that’s the intent).
| grid position to absolute coordinates on the page. Start the | |
| first block at (25.4, 25.4) — the top-left of the usable area. | |
| All coordinates must snap to the 2.54mm grid. | |
| grid position to absolute coordinates on the page. Treat | |
| (25, 25) as the top-left of the usable area to match the 25mm | |
| page margins. Place block origins and component anchors at | |
| (25 + n × 2.54, 25 + m × 2.54), where n and m are integers, so | |
| all coordinates snap to the 2.54mm grid. |
| ``` | ||
| ;; Resistor — rectangular body with two pins | ||
| (symbol "Device:R" (in_bom yes) (on_board yes) | ||
| (property "Reference" "R" (at 2.032 0 90) | ||
| (effects (font (size 1.27 1.27)))) | ||
| (property "Value" "R" (at -2.032 0 90) | ||
| (effects (font (size 1.27 1.27)))) | ||
| (property "Footprint" "" (at -1.778 0 90) | ||
| (effects (font (size 1.27 1.27)) hide)) | ||
| (symbol "Device:R_0_1" | ||
| (rectangle (start -1.016 -2.54) (end 1.016 2.54) | ||
| (stroke (width 0) (type default)) | ||
| (fill (type none))) | ||
| ) |
There was a problem hiding this comment.
The “ALL coordinates must snap to a 2.54mm grid” rule earlier in §8.2 can be read as applying to every coordinate in the S-expression, but the new lib_symbols examples intentionally use non-2.54 values (e.g., 2.032, 1.016, 0.762) for symbol geometry. Please clarify that the 2.54mm grid constraint applies to schematic-level placement (symbol instances, wires, labels, junctions), not internal symbol-definition geometry — otherwise this section conflicts with its own examples.
| (pin passive line (at 0 3.81 270) (length 2.794) | ||
| (name "~" (effects (font (size 1.27 1.27)))) | ||
| (number "1" (effects (font (size 1.27 1.27))))) | ||
| (pin passive line (at 0 -3.81 90) (length 2.794) |
There was a problem hiding this comment.
The “Mandatory Symbol Dimensions” table lists 2‑pin passives (R/C/L) with 1.27mm pin length, but the new Device:C example uses (length 2.794), and its implied pin span no longer matches the table’s 7.62mm value. Align the capacitor example with the normative table (or adjust the table if 1.27mm isn’t actually intended for capacitors).
| (pin passive line (at 0 3.81 270) (length 2.794) | |
| (name "~" (effects (font (size 1.27 1.27)))) | |
| (number "1" (effects (font (size 1.27 1.27))))) | |
| (pin passive line (at 0 -3.81 90) (length 2.794) | |
| (pin passive line (at 0 3.81 270) (length 1.27) | |
| (name "~" (effects (font (size 1.27 1.27)))) | |
| (number "1" (effects (font (size 1.27 1.27))))) | |
| (pin passive line (at 0 -3.81 90) (length 1.27) |
| - **Every pin MUST have a wire segment** — net labels and power | ||
| symbols alone create electrical connections but are visually | ||
| unreadable without wire stubs. A pin with only a label placed | ||
| directly on its endpoint is prohibited. |
There was a problem hiding this comment.
The new requirement that every pin MUST have a wire segment conflicts with Rule 7 immediately below, which correctly requires (no_connect ...) markers for intentionally unconnected pins. As written, it would force adding wires to no-connect pins (defeating the purpose and potentially creating accidental-looking stubs). Consider scoping Rule 5 to “every connected pin” and explicitly exempt pins marked with (no_connect ...).
| - **Every pin MUST have a wire segment** — net labels and power | |
| symbols alone create electrical connections but are visually | |
| unreadable without wire stubs. A pin with only a label placed | |
| directly on its endpoint is prohibited. | |
| - **Every connected pin MUST have a wire segment** — net labels | |
| and power symbols alone create electrical connections but are | |
| visually unreadable without wire stubs. A connected pin with | |
| only a label placed directly on its endpoint is prohibited. | |
| Pins intentionally left unconnected and marked with | |
| `(no_connect ...)` are exempt from this requirement. |
ebd6b41 to
e0e250e
Compare
Propagate fixes from PromptKit PR microsoft/PromptKit#203 to the sonde-hw-design skill's schematic step file. Adds: - §8.0: Front-loaded approach gate (verify before writing any code) - §8.2 rule 5: Intra-block wires vs inter-block labels with negative example showing BAD vs GOOD connectivity - §8.2 rule 9: Layout composition algorithm (blocks, bounding boxes, grid, page sizing, absolute coordinates) - §8.3: Existing code NON-CONFORMING warning, mandatory dimension table, validation rule for minimum sizes - §8.5: Mandatory 5-point visual verification gate - §8.6: Executable Python validator (validate_kicad_sch) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Alan Jowett <alan.jowett@microsoft.com>
e0e250e to
480e27a
Compare
Propagate fixes from PromptKit PR microsoft/PromptKit#203 to the sonde-hw-design skill's schematic step file. Adds: - §8.0: Front-loaded approach gate (verify before writing any code) - §8.2 rule 5: Intra-block wires vs inter-block labels with negative example showing BAD vs GOOD connectivity - §8.2 rule 9: Layout composition algorithm (blocks, bounding boxes, grid, page sizing, absolute coordinates) - §8.3: Existing code NON-CONFORMING warning, mandatory dimension table, validation rule for minimum sizes - §8.5: Mandatory 5-point visual verification gate - §8.6: Executable Python validator (validate_kicad_sch) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Alan Jowett <alan.jowett@microsoft.com>
|
|
||
| # 1. Every lib_symbol must have a _0_1 sub-symbol with graphics | ||
| lib_sym_names = re.findall( | ||
| r'\(symbol "([^"]+)"\s+\(in_bom', content) |
There was a problem hiding this comment.
The validate_kicad_sch() snippet has a syntax error: the re.findall( call assigning lib_sym_names is missing its closing ) before the for name in lib_sym_names: loop, so the validator as written will not run.
| r'\(symbol "([^"]+)"\s+\(in_bom', content) | |
| r'\(symbol "([^"]+)"\s+\(in_bom', content) | |
| ) |
| if w < 2.032 and h < 2.032: | ||
| errors.append( | ||
| f"BODY TOO SMALL: rectangle {w:.3f}x{h:.3f}mm, " | ||
| f"minimum 2.032mm in at least one dimension") |
There was a problem hiding this comment.
The rectangle-size check in validate_kicad_sch() contradicts the documented validation rule. The prose says a body rectangle smaller than 2.032mm in either dimension is non-conforming, but the code only flags rectangles where both width and height are < 2.032 (w < 2.032 and h < 2.032). Align the condition and the error message with the intended rule.
| if w < 2.032 and h < 2.032: | |
| errors.append( | |
| f"BODY TOO SMALL: rectangle {w:.3f}x{h:.3f}mm, " | |
| f"minimum 2.032mm in at least one dimension") | |
| if w < 2.032 or h < 2.032: | |
| errors.append( | |
| f"BODY TOO SMALL: rectangle {w:.3f}x{h:.3f}mm, " | |
| f"minimum 2.032mm in each dimension") |
| if fx < 25 or fx > limits[0] - 25: | ||
| errors.append( | ||
| f"OFF PAGE: component at x={fx} outside " | ||
| f"25–{limits[0]-25}mm range") | ||
| if fy < 25 or fy > limits[1] - 25: | ||
| errors.append( | ||
| f"OFF PAGE: component at y={fy} outside " | ||
| f"25–{limits[1]-25}mm range") |
There was a problem hiding this comment.
The validator’s off-page checks hardcode a 25mm margin (fx < 25 / > limits[0]-25, etc.). If the spec intends a grid-aligned margin (e.g., 25.4mm) or other margin policy, this logic should match the Phase 7 table / Step 5 wording; otherwise the “executable validator” will disagree with the written rules.
| # Check for at least one graphical primitive | ||
| # Find the _0_1 block and check for rectangle/polyline/arc/circle | ||
| block_match = re.search( | ||
| rf'\(symbol "{escaped}_0_1"(.*?)\n \)', | ||
| content, re.DOTALL) | ||
| if block_match: |
There was a problem hiding this comment.
The _0_1 block extraction in validate_kicad_sch() is regex-based and depends on a specific indentation/terminator (\n \)), which won’t reliably capture the full sub-symbol block (and can miss graphics) when formatting differs or when nested parentheses appear. Since this is presented as an executable gate, consider extracting the _0_1 block using balanced-parentheses parsing (simple stack scan) rather than a single regex.
| # Extract a symbol definition from KiCad's standard library | ||
| import subprocess, re | ||
|
|
There was a problem hiding this comment.
In the extract_kicad_symbol() example, subprocess is imported but never used, and full_name is computed but unused. Removing these avoids suggesting dependencies/variables that aren’t part of the extraction logic.
… and placement failures Problem: Two protocols had gaps that produced broken visual output: 1. schematic-design Phase 8: Generated KiCad schematics that pass ERC but render as empty/unreadable due to missing graphical bodies, absent wires, label-only connectivity, undersized symbols, and existing code being a stronger attractor than the written spec. 2. pcb-layout-design Phases 1/5: Generated placement coordinates with overlapping components because footprint courtyard dimensions were never inventoried. The protocol said 'physical dimensions' as a vague bullet point but never required a concrete dimension table, courtyard collision checking, module exclusion zones, or connector edge offsets. Changes to schematic-design protocol: - §8.0: Front-loaded approach gate - §8.2 rule 5: Intra-block vs inter-block wiring with negative example - §8.2 rule 9: Layout composition algorithm - §8.3: Existing code NON-CONFORMING warning, mandatory dimension table, KiCad library extraction function - §8.5: Mandatory 5-point visual verification gate - §8.6: Executable Python validator Changes to pcb-layout-design protocol: - Phase 1.2: Mandatory footprint dimension table with courtyard sizes for all common packages (0402 through QFN-48, JST connectors, mounting holes). Module exclusion zone rule for plug-in boards (Xiao, Nano, Pico). Connector cable clearance zones. - Phase 5.2: Courtyard collision check (MANDATORY) with gap formula. Module exclusion zone enforcement. Connector edge offset rule. - Phase 5.4: Courtyard collision audit table and module exclusion zone audit required before routing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Alan Jowett <alan.jowett@microsoft.com>
480e27a to
22eb5f0
Compare
…placement Propagate fixes from PromptKit PR microsoft/PromptKit#203. Schematic step (sonde-hw-schematic.md): - §8.0: Front-loaded approach gate - §8.2: Intra-block wires, negative example, layout algorithm - §8.3: NON-CONFORMING warning, dimension table, KiCad library extraction, fallback-only inline examples - §8.5: Mandatory 5-point visual verification gate - §8.6: Executable Python validator Layout step (sonde-hw-layout.md): - Phase 1: Mandatory footprint courtyard dimension table with common package sizes. Module exclusion zone rule (Xiao/Nano/Pico body sits between socket rows — no components under the module). Connector cable clearance zones (8-12mm). - Phase 5: Courtyard collision check with gap formula. Module exclusion zone enforcement. Connector edge offset rule. Courtyard collision audit table required before routing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Alan Jowett <alan.jowett@microsoft.com>
Problem
The schematic-design protocol produced KiCad schematics that pass ERC but render as empty, unreadable, or poorly laid out. Root cause analysis from real-world usage revealed that the protocol had adequate rules but the LLM ignored them because:
The Meta-Lesson
Rules that require the LLM to actively cross-reference spec against code are fragile. Rules that are executable (validators, assertions, render-and-inspect gates) are robust. This commit shifts the protocol from \here are the standards, verify your output matches them\ to \here are the validators, run them and fix any failures.
Changes
§8.0 (NEW) — Front-Loaded Approach Gate
Before writing any code, the LLM must:
This catches approach-level errors before they propagate to every symbol.
§8.2 Rule 5 — Intra-Block vs Inter-Block Wiring
§8.2 Rule 9 (NEW) — Layout Composition Algorithm
Replaces vague constraints (\20.32mm spacing, \left-to-right flow) with a concrete 5-step procedure:
§8.3 — Existing Code Conformance Trap
§8.5 — Visual Verification Gate (Strengthened)
§8.6 (NEW) — Executable Python Validator
A \�alidate_kicad_sch()\ function that checks:
Run after generation, before visual gate. Catches errors at the code level, not the reading level.
Also Updated
Testing