app
AI Flow
Public Made by Adomby adom
Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board.
← Commit history
Honor reviewed thermal via counts without adding optional holes
4 files changed
+32−1
crates/adom-aiflow/src/main.rs+1−1@@ -3567,7 +3567,7 @@ fn main() { let b = if action == "plan" { fusion_live(&r, &cli, &dir).map(|l| Board::load(&l.1).unwrap_or_else(|e| err(&e, &[]))) } else { None }.unwrap_or_else(|| board_of(&r)); let sw = switchnode::nets(&spec, &b); if action == "plan" {- let (parts, plan) = thermalvias::plan(&spec, &b, &sw);+ let (parts, plan) = thermalvias::plan_with_acknowledgements(&spec, &b, &sw, &acked); let pp = dir.join("plan-thermal-vias.json"); write_json(&pp, &plan); let lines: Vec<String> = parts.iter().map(|p| format!(" {:<5} {:<5} target {:>2} existing {:>2} planned {:>2}{}", p.reference, p.net, p.target, p.existing, p.planned.len(), p.note.as_ref().map(|n| format!(" ({n})")).unwrap_or_default())).collect();
crates/adom-aiflow/src/thermalvias.rs+20@@ -58,6 +58,12 @@ pub struct Part { pub reference: String, pub net: String, pub target: usize, pub /// Plan the vias. `poured_both` says whether a net has copper on both outer layers (a via only moves /// heat where the net has copper on the other side). pub fn plan(spec: &Value, b: &Board, sw_nets: &[String]) -> (Vec<Part>, Value) {+ plan_with_acknowledgements(spec, b, sw_nets, &[])+}++/// Preserve reviewed targets/counts without proposing optional holes for an+/// explicitly acknowledged reference. Cooling and all downstream gates remain.+pub fn plan_with_acknowledgements(spec: &Value, b: &Board, sw_nets: &[String], acked: &[String]) -> (Vec<Part>, Value) { let size = spec["viaPower"].as_array().and_then(|a| a.first()).and_then(|v| v.as_f64()).or(spec["viaPower"].as_f64()).unwrap_or(0.6); let drill = spec["viaPower"].as_array().and_then(|a| a.get(1)).and_then(|v| v.as_f64()).or(spec["drillPower"].as_f64()).unwrap_or(0.3); let clearance = spec["clearance"].as_f64().unwrap_or(0.2);@@ -92,6 +98,11 @@ pub fn plan(spec: &Value, b: &Board, sw_nets: &[String]) -> (Vec<Part>, Value) { let near = |x: f64, y: f64| pads.iter().any(|p| (p.x - x).hypot(p.y - y) < 3.0); part.existing = vias.iter().filter(|v| v.net == net && near(v.x, v.y) && local_pours(&pours, &net, (v.x, v.y), v.size / 2.0, &b.outline)).count();+ if acked.contains(&reference) {+ part.note = Some(format!("reviewed via-count acknowledgement for {reference}: retain {} existing vias; generic target {tgt} remains reported, cooling acceptance is separate", part.existing));+ parts.push(part);+ continue;+ } let need = tgt.saturating_sub(part.existing); // candidate spots on rings around each heat pad, nearest first let mut cands: Vec<(f64, (f64, f64))> = Vec::new();@@ -181,6 +192,15 @@ mod tests { "pours":[{"net":"GND","layers":["F.Cu","B.Cu"],"polygon":[[8,8],[12,8],[12,12],[8,12]]}]}); let (parts, _) = super::plan(&spec, &b, &[]); assert!(!parts[0].planned.is_empty());+ let (reviewed, reviewed_plan) = super::plan_with_acknowledgements(&spec, &b, &[], &["U1".into()]);+ assert_eq!(reviewed[0].target, parts[0].target);+ assert_eq!(reviewed[0].existing, parts[0].existing);+ assert!(reviewed[0].planned.is_empty());+ assert!(reviewed_plan["nets"].as_array().unwrap().is_empty());+ assert!(!super::short(&spec, &b, &[]).is_empty());+ assert!(super::short(&spec, &b, &["U1".into()]).is_empty());+ let (unrelated, _) = super::plan_with_acknowledgements(&spec, &b, &[], &["UNKNOWN".into()]);+ assert_eq!(unrelated[0].planned, parts[0].planned); let mut landed = text[..text.len()-1].to_string(); for &(x,y) in &parts[0].planned { assert!((x-10.0).hypot(y-10.0) < 3.0);
docs/thermal-via-acknowledgements.mdadded+7@@ -0,0 +1,7 @@+# Reviewed thermal-via counts++`thermal-vias plan` respects existing explicit `--ack REF --why` reviews. It reports the generic target and actual qualifying count, while leaving new optional holes out of the plan for that exact reference. Other parts retain normal planning and acceptance. Removing the acknowledgement restores normal proposals.++An acknowledgement reviews the via count only. Document the saved board, physical heat path, watts, local copper, barrel assumptions and remaining cooling requirements. A TO-220 external case path is not proved by a drain-pad via count. Native DRC, same-input Fields, current, thermal and finish checks remain mandatory; this feature grants no cooling credit. Never use it to hide an unresolved thermal failure.++Refresh the plan after adding or changing an acknowledgement; a previously generated plan is not silently rewritten by `land`.
skills/adom-aiflow/SKILL.md+4@@ -411,3 +411,7 @@ Read `skills/aiflow-project-page/SKILL.md` and run `adom-aiflow project-page-che ### Board-edge connectors A USB or other edge connector may intentionally project its mechanical courtyard past a straight board edge. Do not move its mating face inward merely to satisfy a bounding-box check. `place verify` supports a reviewed `placement.edgeConnectors` entry keyed by reference, with exact `footprint`, `edge` (`minX`, `maxX`, `minY`, `maxY`), positive `maxCourtyardOverhangMm`, and a concrete `reason`. Read `docs/edge-connectors.md`. The complete protruding span must lie on a straight native outline segment, every pad polygon must stay inside the PCB, saved/live poses must match, and native placement DRC must be complete and clean. This does not waive copper, hole, silk, courtyard-overlap, mechanical mating or full-board DRC requirements. Unsupported outline geometry and unlisted protrusions remain failures.+++### Reviewed via counts do not add optional holes+`thermal-vias plan` honors explicit via-count acknowledgements for named references, retaining reported targets and actual counts while omitting their new optional holes. Read `docs/thermal-via-acknowledgements.md`; refresh the plan after a review. Record exact-board heat-path and barrel assumptions. This does not pass current or thermal analysis or bypass native DRC or finish.