Skip to content

fix(schematic-design): add lib_symbols graphical body requirement#203

Closed
Alan-Jowett wants to merge 1 commit intomicrosoft:mainfrom
Alan-Jowett:fix/schematic-lib-symbols-graphical-body
Closed

fix(schematic-design): add lib_symbols graphical body requirement#203
Alan-Jowett wants to merge 1 commit intomicrosoft:mainfrom
Alan-Jowett:fix/schematic-lib-symbols-graphical-body

Conversation

@Alan-Jowett
Copy link
Copy Markdown
Member

@Alan-Jowett Alan-Jowett commented Apr 7, 2026

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:

  • Existing repo code was a stronger attractor than the written spec
  • Declarative rules lost to executable code patterns
  • Validation was deferred to post-generation, catching errors too late
  • Symbol dimensions were presented as examples, not mandates
  • No negative examples showed what bad output looks like
  • No executable validator caught errors at the code level

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:

  1. Build one symbol and verify it against the dimension table
  2. Compute bounding boxes and verify they fit the page
  3. Check existing code against the spec before reusing it

This catches approach-level errors before they propagate to every symbol.

§8.2 Rule 5 — Intra-Block vs Inter-Block Wiring

  • Within a functional block: direct wires (reader can trace without reading labels)
  • Between blocks: net labels on wire stubs
  • Negative example added: ASCII art showing BAD (label-only, no wires) vs GOOD (direct wire within block, label between blocks)

§8.2 Rule 9 (NEW) — Layout Composition Algorithm

Replaces vague constraints (\20.32mm spacing, \left-to-right flow) with a concrete 5-step procedure:

  1. Define functional blocks
  2. Compute each block's bounding box
  3. Arrange blocks in a grid (power left, MCU center, peripherals right)
  4. Determine page size from total extent (table: ≤15→A4, 16–40→A3, 41–80→A2)
  5. Assign absolute coordinates on the 2.54mm grid

§8.3 — Existing Code Conformance Trap

  • Warning elevated from informational to ⚠️ NON-CONFORMING blockquote
  • Specific failure patterns called out: undersized bodies (0.762mm vs 2.032mm), missing _0_1\ sub-symbols, label-only connectivity, short pins
  • Mandatory symbol dimension table (normative, not examples): body sizes, pin spans, pin lengths, minimum spacing per symbol type
  • Validation rule: body < 2.032mm or pin length < 1.27mm = non-conforming, must be resized

§8.5 — Visual Verification Gate (Strengthened)

  • MANDATORY — CANNOT be skipped
  • 5-point render-and-inspect: visible symbols at legible size, visible wires with intra-block direct connections, within page, readable labels, functional block grouping
  • Fix guidance includes \do NOT reuse undersized dimensions from existing code\

§8.6 (NEW) — Executable Python Validator

A \�alidate_kicad_sch()\ function that checks:

  1. Every \lib_symbol\ has a _0_1\ sub-symbol with graphical primitives
  2. All pin lengths ≥ 1.27mm
  3. All body rectangles ≥ 2.032mm in at least one dimension
  4. At least one (wire ...)\ entry exists
  5. All component placements within page bounds (25mm margins)

Run after generation, before visual gate. Catches errors at the code level, not the reading level.

Also Updated

  • §8.1: Two-level sub-symbol structure (_0_1/_1_1) documented with templates
  • §8.2 rule 6: Label placement strengthened
  • Phase 7 rule 5: Page boundary calculation table
  • §8.4: Checklist items for graphical body, wire existence, page boundary

Testing

  • Visual: a schematic generated following the updated protocol should render with correctly sized symbols, direct intra-block wires, clear block grouping, and readable labels in KiCad 7+
  • The executable validator (\�alidate_kicad_sch()) can be run against any generated .kicad_sch\ file
  • No existing automated tests to run (protocol is prose)

Copilot AI review requested due to automatic review settings April 7, 2026 02:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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_1 graphical-body sub-symbol and a _1_1 pin sub-symbol.
  • Adds guidance on the two-level sub-symbol structure plus pin coordinate/angle rules.
  • Provides embedded lib_symbols examples for Device:R, Device:C, and a generic custom IC, and extends the generation checklist accordingly.

Comment on lines +396 to +398
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).
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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).

Copilot uses AI. Check for mistakes.
Comment on lines +493 to +497
(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)
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
Comment on lines +549 to +552
(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)
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
(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)

Copilot uses AI. Check for mistakes.
@Alan-Jowett Alan-Jowett force-pushed the fix/schematic-lib-symbols-graphical-body branch from beee133 to e815891 Compare April 7, 2026 02:51
Copilot AI review requested due to automatic review settings April 7, 2026 03:04
@Alan-Jowett Alan-Jowett force-pushed the fix/schematic-lib-symbols-graphical-body branch from e815891 to ebd6b41 Compare April 7, 2026 03:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 4 comments.

Comment on lines +530 to +532
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.
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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).

Suggested change
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.

Copilot uses AI. Check for mistakes.
Comment on lines +593 to +606
```
;; 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)))
)
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +630 to +633
(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)
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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).

Suggested change
(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)

Copilot uses AI. Check for mistakes.
Comment on lines +446 to +449
- **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.
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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 ...).

Suggested change
- **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.

Copilot uses AI. Check for mistakes.
@Alan-Jowett Alan-Jowett force-pushed the fix/schematic-lib-symbols-graphical-body branch from ebd6b41 to e0e250e Compare April 7, 2026 03:14
Alan-Jowett pushed a commit to Alan-Jowett/sonde that referenced this pull request Apr 7, 2026
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>
Copilot AI review requested due to automatic review settings April 7, 2026 03:28
@Alan-Jowett Alan-Jowett force-pushed the fix/schematic-lib-symbols-graphical-body branch from e0e250e to 480e27a Compare April 7, 2026 03:28
Alan-Jowett pushed a commit to Alan-Jowett/sonde that referenced this pull request Apr 7, 2026
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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 5 comments.


# 1. Every lib_symbol must have a _0_1 sub-symbol with graphics
lib_sym_names = re.findall(
r'\(symbol "([^"]+)"\s+\(in_bom', content)
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
r'\(symbol "([^"]+)"\s+\(in_bom', content)
r'\(symbol "([^"]+)"\s+\(in_bom', content)
)

Copilot uses AI. Check for mistakes.
Comment on lines +963 to +966
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")
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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")

Copilot uses AI. Check for mistakes.
Comment on lines +985 to +992
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")
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +935 to +940
# 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:
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +638 to +640
# Extract a symbol definition from KiCad's standard library
import subprocess, re

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
… 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>
@Alan-Jowett Alan-Jowett force-pushed the fix/schematic-lib-symbols-graphical-body branch from 480e27a to 22eb5f0 Compare April 7, 2026 03:49
Alan-Jowett pushed a commit to Alan-Jowett/sonde that referenced this pull request Apr 7, 2026
…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>
@Alan-Jowett Alan-Jowett closed this Apr 7, 2026
@Alan-Jowett Alan-Jowett deleted the fix/schematic-lib-symbols-graphical-body branch April 7, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants