Closed bug report

place verify refuses nested copper-only footprints on box overlaps that KiCad's exact DRC passes

John Lauer · ·closed by John Lauer

What happens

adom-aiflow place verify (0.2.6, unchanged in 0.2.7) refuses a correct placement with "native placement has unresolved courtyardOverlaps" (and "withoutCourtyard") when a board has nested copper-only footprints, even though KiCad's exact placement DRC on the same live board is clean.

The board was a rotary encoder sensor: generated electrode rings, a guard ring and mounting holes are footprints (listed in spec.fixedRefs) that sit inside one another. The bridge's courtyardOverlaps judges courtyards as bounding boxes, and a footprint without a courtyard by its pad extent, so concentric rings always overlap as boxes. The verdict then also fails its own consistency check (placed vs. outsideOutline).

Cause

crates/adom-aiflow/src/placement_verify.rs, around line 88:

if validation["placed"] != Value::Bool(outside.is_empty()) {return Err("inconsistent native placement verdict".into())}
for key in ["parked","courtyardOverlaps","withoutCourtyard"] {
    if !validation[key].as_array().is_some_and(|a|a.is_empty()) { return Err(format!("native placement has unresolved {key}")); }
}

Any box overlap refuses, with no way to say "these are generated, locked copper and KiCad's exact check is clean".

Fix we used

Accept box overlaps only when both parts are in spec.fixedRefs and KiCad's own DRC (already required by the same function) reports no courtyard problem; accept a missing courtyard only for fixedRefs; refuse everything else, and name the offending pairs in the error:

let fixed:BTreeSet<&str>=spec["fixedRefs"].as_array().map(|a|a.iter().filter_map(Value::as_str).collect()).unwrap_or_default();
let overlaps=validation["courtyardOverlaps"].as_array().ok_or("missing courtyardOverlaps")?;
let open_overlaps:Vec<&Value>=overlaps.iter().filter(|o|!(fixed.contains(o["a"].as_str().unwrap_or("")) && fixed.contains(o["b"].as_str().unwrap_or("")))).collect();
let no_cy=validation["withoutCourtyard"].as_array().ok_or("missing withoutCourtyard")?;
let open_no_cy:Vec<&Value>=no_cy.iter().filter(|r|!fixed.contains(r.as_str().unwrap_or(""))).collect();
let placed_box = outside.is_empty() && overlaps.is_empty();
if validation["placed"] != Value::Bool(placed_box) {return Err("inconsistent native placement verdict".into())}
if !validation["parked"].as_array().is_some_and(|a|a.is_empty()) { return Err("native placement has unresolved parked".into()); }
if !open_overlaps.is_empty() { return Err(format!("native placement has unresolved courtyardOverlaps: {}", serde_json::to_string(&open_overlaps).unwrap_or_default())); }
if !open_no_cy.is_empty() { return Err(format!("native placement has unresolved withoutCourtyard: {}", serde_json::to_string(&open_no_cy).unwrap_or_default())); }

A cleaner long-term fix may be for the KiCad bridge to report exact-shape courtyard overlaps; that is the bridge's call. Either way, the error should name the pairs: "unresolved courtyardOverlaps" alone sent us digging.

Patch

aiflow/tool-local/aiflow-0.2.6-capencoder.patch on john/capacitive-encoder (private; the hunk above is the whole fix): https://wiki.adom.inc/john/capacitive-encoder

Found by a Claude Opus 5.5 thread running the KiCad lane on 2026-10-20; with the fix, 41 placements verified live.

1 Reply

John Lauer ·

Fixed in adom-aiflow 0.2.8, along the lines you proposed, with one extra guard.

  • What placed means: the KiCad bridge defines it as "no courtyard box overlaps and nothing outside the outline" (kicad-core validate), and the consistency check now uses exactly that.

  • When a box overlap is excused: only when all three hold:

    1. both references are in spec.fixedRefs;
    2. KiCad's placement DRC is clean (already required);
    3. AI Flow's own exact-shape courtyard check (the #78 even-odd rule) finds no overlap for that pair.

    The third condition is the addition. A project can set courtyards_overlap to ignore, and then a clean DRC proves nothing.

  • When a missing courtyard is excused: only for a fixedRefs footprint with no pads, the same rule the gate uses. KiCad's missing_courtyard check is off by default, so a clean DRC can't vouch for it.

  • Errors and records: every refusal names the offending pairs, references or parked parts. Excused findings are recorded in the place-land outcome under excused and summarised on the OK line.

  • Tests:

    • an all-fixed pair is excused and recorded
    • the same pair is refused when DRC is not clean
    • a pair with a non-fixed reference is refused and named
    • fixed references whose real courtyards overlap are still refused
    • a missing courtyard is excused only for padless fixed references

Your other idea, exact-shape overlaps in the bridge itself, would let this go back to trusting the bridge. That is kicad-bridge's call.

Upgrade: adom-wiki pkg install adom/adom-aiflow, then adom-aiflow --version says 0.2.8.

John Lauer closed this ·

Log in to reply.