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
Require drill-void and plated-wall current evidence; preserve native copper net intent
10 files changed
+91−15
Cargo.lock+10−10@@ -4,7 +4,7 @@ version = 4 [[package]] name = "adom-aiflow"-version = "0.1.80"+version = "0.1.81" dependencies = [ "aiflow-analyze", "aiflow-board",@@ -24,7 +24,7 @@ dependencies = [ [[package]] name = "aiflow-analyze"-version = "0.1.80"+version = "0.1.81" dependencies = [ "aiflow-board", "serde",@@ -34,7 +34,7 @@ dependencies = [ [[package]] name = "aiflow-board"-version = "0.1.80"+version = "0.1.81" dependencies = [ "roxmltree", "serde",@@ -43,7 +43,7 @@ dependencies = [ [[package]] name = "aiflow-bridge"-version = "0.1.80"+version = "0.1.81" dependencies = [ "aiflow-board", "serde",@@ -52,7 +52,7 @@ dependencies = [ [[package]] name = "aiflow-copper"-version = "0.1.80"+version = "0.1.81" dependencies = [ "aiflow-board", "aiflow-grid",@@ -62,7 +62,7 @@ dependencies = [ [[package]] name = "aiflow-grid"-version = "0.1.80"+version = "0.1.81" dependencies = [ "aiflow-board", "serde",@@ -71,7 +71,7 @@ dependencies = [ [[package]] name = "aiflow-place"-version = "0.1.80"+version = "0.1.81" dependencies = [ "aiflow-board", "serde",@@ -80,7 +80,7 @@ dependencies = [ [[package]] name = "aiflow-pours"-version = "0.1.80"+version = "0.1.81" dependencies = [ "aiflow-board", "aiflow-copper",@@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "aiflow-router"-version = "0.1.80"+version = "0.1.81" dependencies = [ "aiflow-board", "aiflow-grid",@@ -100,7 +100,7 @@ dependencies = [ [[package]] name = "aiflow-run"-version = "0.1.80"+version = "0.1.81" dependencies = [ "serde", "serde_json",
Cargo.toml+1−1@@ -14,7 +14,7 @@ members = [ ] [workspace.package]-version = "0.1.80"+version = "0.1.81" edition = "2021" license = "MIT" repository = "https://wiki.adom.inc/adom/adom-aiflow"
SKILL.md+6@@ -377,3 +377,9 @@ When a narrow sense or parallel branch causes a full-net width warning, read `do ### Unclipped Fields current maxima Current acceptance requires `peakJDefinition` declaring an unclipped, unrounded cell maximum with a copper-fraction cutoff no greater than 0.25. Earlier Fields reports clipped peaks for visualization and can hide current-density failures. Do not copy the new marker onto an old report: rerun a reviewed solver with the reporting fix. Owner review, release and installed availability remain separate; a pending source fix is not a qualified board. Keep the failed gate visible while correcting bottlenecks and continue other authorized work.++### Plated-hole current review (0.1.81)++A current report must identify distributed plated-wall coupling with removed drill voids and include every plated connection. AIFlow independently checks signed current divided by barrel area, compares it with the barrel-density limit, and rejects `current-barrel` findings even when surface current passes. Old center-cell reports can contain apparent surface hotspots inside drill holes; do not alter the PCB merely to chase that artifact or stamp new metadata onto cached output. The corrected Fields implementation is under owner review in adom/adom-fields PR 9 (dependencies 8, 6 and 5). Preserve shared runtime pins and continue other authorized work while an owner review is pending.++For native saved-board experiments, preserve a UUID-to-net map before and after connectivity build, refill, save and reload. KiCad can reassign a crossing copper item's net during connectivity repair; zero DRC errors alone do not prove the intended circuit. Reject changed intent, correct geometry and rerun. Keep the live board unchanged until the candidate's geometry, net assignments and engineering checks are reviewed.
bin/adom-aiflow⋯ 1 unchanged line ⋯
crates/aiflow-analyze/src/lib.rs+53−2@@ -255,6 +255,34 @@ pub fn field_screen(fields: &Value, spec: &Value, board_bytes: &[u8], spec_bytes if !density_limit.is_finite() || density_limit<=0.0 || peak.unwrap()>density_limit { return Err(format!("Fields {name} peak current density exceeds the {density_limit} A/mm2 screen")); }+ let coupling=&fields["barrelCoupling"];+ if coupling["model"]!="distributed-plated-wall" || coupling["drillVoidsRemoved"]!=true+ || coupling["platingUm"].as_f64()!=Some(25.0) {+ return Err("Fields lacks the reviewed plated-wall/drill-void model. Older center-cell coupling can report peaks inside holes. Rerun with the reviewed Fields correction (adom/adom-fields PR 9 and dependencies); preserve owner review and do not relabel cached reports".into());+ }+ let connections=row["platedConnections"].as_array().ok_or_else(||format!("Fields {name} has no plated-connection report"))?;+ let layers=row["layers"].as_array().ok_or("Fields net layers are missing")?;+ let vias=row["vias"].as_u64().ok_or("Fields via count is missing")?;+ let pads=row["platedPads"].as_u64().ok_or("Fields plated-pad count is missing")?;+ if layers.is_empty() {return Err("Fields net has no copper layers".into());}+ let expected=vias.checked_add(pads).and_then(|n|n.checked_mul(layers.len().saturating_sub(1) as u64)).ok_or("Fields connection count overflow")?;+ if connections.len() as u64!=expected {return Err(format!("Fields {name} omitted plated connections"));}+ let barrel_limit=spec["branchCurrentModel"]["maxBarrelDensityAperMm2"].as_f64().unwrap_or(density_limit);+ if !barrel_limit.is_finite() || barrel_limit<=0.0 {return Err("barrel density limit must be positive".into());}+ for c in connections {+ let amps=c["amps"].as_f64().filter(|v|v.is_finite());+ let area=c["barrelAreaMm2"].as_f64().filter(|v|v.is_finite() && *v>0.0);+ let density=c["meanBarrelJ"].as_f64().filter(|v|v.is_finite() && *v>=0.0);+ if amps.is_none() || area.is_none() || density.is_none()+ || !c["resolvedPerimeterFraction"].as_f64().is_some_and(|v|v.is_finite() && v>0.0 && v<=1.0+1e-9)+ || c["from"]==c["to"] || !layers.contains(&c["from"]) || !layers.contains(&c["to"]) {+ return Err(format!("Fields {name} has an incomplete plated-connection solve"));+ }+ let actual=amps.unwrap().abs()/area.unwrap();+ if (actual-density.unwrap()).abs()>1e-8*actual.max(1.0) || actual>barrel_limit {+ return Err(format!("Fields {name} barrel density is inconsistent or exceeds {barrel_limit} A/mm2"));+ }+ } if value["currentModel"] == "resistive-branches" { let terminals = &fields["terminalAssignments"][name]; if terminals["mode"] != "explicit" { return Err(format!("Fields {name} used heuristic terminals; solve the declared explicit pins")); }@@ -273,7 +301,7 @@ pub fn field_screen(fields: &Value, spec: &Value, board_bytes: &[u8], spec_bytes } } let issues=fields["issues"].as_array().ok_or("Fields report has no issues array")?;- let relevant: Vec<_>=issues.iter().filter(|i| if kind=="current" { i["kind"]=="current-neck" } else { i["kind"]=="tab-rise" || i["kind"]=="thermal-vias" }).collect();+ let relevant: Vec<_>=issues.iter().filter(|i| if kind=="current" { i["kind"]=="current-neck" || i["kind"]=="current-barrel" } else { i["kind"]=="tab-rise" || i["kind"]=="thermal-vias" }).collect(); if !relevant.is_empty() { return Err(relevant.iter().map(|i|i["what"].as_str().unwrap_or("unresolved field finding")).collect::<Vec<_>>().join("; ")); } Ok(()) }@@ -282,7 +310,30 @@ pub fn field_screen(fields: &Value, spec: &Value, board_bytes: &[u8], spec_bytes mod field_screen_tests { use super::*; use sha2::{Digest,Sha256};- fn report() -> Value { serde_json::json!({"peakJDefinition":{"statistic":"unclipped-cell-maximum","rounded":false,"minimumCopperFraction":0.25},"inputIdentity":{"board":format!("{:x}",Sha256::digest(b"board")),"spec":format!("{:x}",Sha256::digest(b"spec"))},"nets":[{"net":"VCC","amps":2,"residual":1e-8,"peakJ":20}],"chips":[{"reference":"Q1"}],"issues":[]}) }+ fn report() -> Value { serde_json::json!({"barrelCoupling":{"model":"distributed-plated-wall","drillVoidsRemoved":true,"platingUm":25},"peakJDefinition":{"statistic":"unclipped-cell-maximum","rounded":false,"minimumCopperFraction":0.25},"inputIdentity":{"board":format!("{:x}",Sha256::digest(b"board")),"spec":format!("{:x}",Sha256::digest(b"spec"))},"nets":[{"net":"VCC","amps":2,"residual":1e-8,"peakJ":20,"layers":["F.Cu"],"vias":0,"platedPads":0,"platedConnections":[]}],"chips":[{"reference":"Q1"}],"issues":[]}) }+ #[test]+ fn old_barrel_model_and_omitted_connections_cannot_pass() {+ let spec=json!({"loads":{"VCC":{"amps":2}}});+ for model in [Value::Null,json!({"model":"center-cell"}),json!({"model":"distributed-plated-wall","drillVoidsRemoved":false,"platingUm":25})] {+ let mut f=report();f["barrelCoupling"]=model;+ assert!(field_screen(&f,&spec,b"board",b"spec","current").is_err());+ }+ let mut f=report();f["nets"][0]["layers"]=json!(["F.Cu","B.Cu"]);f["nets"][0]["vias"]=json!(1);+ assert!(field_screen(&f,&spec,b"board",b"spec","current").unwrap_err().contains("omitted plated"));+ }+ #[test]+ fn overloaded_or_inconsistent_barrel_fails_even_without_issue_text() {+ let spec=json!({"loads":{"VCC":{"amps":2}}});let mut f=report();+ f["nets"][0]["layers"]=json!(["F.Cu","B.Cu"]);f["nets"][0]["vias"]=json!(1);+ f["nets"][0]["platedConnections"]=json!([{"from":"F.Cu","to":"B.Cu","amps":2,"barrelAreaMm2":0.05,"meanBarrelJ":40,"resolvedPerimeterFraction":1}]);+ field_screen(&f,&spec,b"board",b"spec","current").unwrap();+ for (key,val) in [("barrelAreaMm2",json!(0.01)),("meanBarrelJ",json!(0)),("resolvedPerimeterFraction",json!(0)),("amps",Value::Null)] {+ let mut bad=f.clone();bad["nets"][0]["platedConnections"][0][key]=val;+ assert!(field_screen(&bad,&spec,b"board",b"spec","current").is_err());+ }+ f["issues"]=json!([{"kind":"current-barrel","what":"unresolved barrel finding"}]);+ assert!(field_screen(&f,&spec,b"board",b"spec","current").unwrap_err().contains("unresolved barrel"));+ } #[test] fn display_clipped_or_rounded_peaks_cannot_pass_acceptance() { let spec=json!({"loads":{"VCC":{"amps":2}}});
docs/release-0.1.81.mdadded+7@@ -0,0 +1,7 @@+# 0.1.81 — plated-hole current evidence++Current acceptance now requires the distributed plated-wall/drill-void report, a complete connection inventory and finite, consistent per-barrel current density. It independently rejects overloads and unresolved current-barrel issues. The finite-cell and material/terminal assumptions still require engineering review; this does not qualify a board or thermal assembly.++Source dependencies: Fields PR 9, stacked on 8, 6 and 5, remains owner-reviewed rather than installed by this release. Older current reports require a fresh corrected solve. Skills also require native copper-net intent checks across connectivity/refill/save/reload, because DRC alone can miss unintended net reassignment.++Validation: production check and release build; 130 workspace tests; real CLI continuation/dashboard workflows against isolated fixtures; component/widget integration and silkscreen geometry checks. All 57 CLI commands are preserved. The electronic-load candidate independently passes native KiCad DRC with zero errors, opens and warnings, but current/thermal acceptance remains unresolved. No completion claim is made by this software release.
package.json+1−1@@ -1,7 +1,7 @@ { "slug": "adom-aiflow", "type": "app",- "version": "0.1.80",+ "version": "0.1.81", "title": "AI Flow", "description": "Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board. One Rust binary with a crate per step (placement helpers, a grid router with Kelvin taps, pours with keepouts, KiCad's DRC gate, live landing through the KiCad Bridge, copper measurement, current and thermal analysis) and a finish line that refuses an unfinished board. Every command answers with hints for the AI; every turn, its thinking time and every rework loop go into run.jsonl, so Claude, Codex and any other engine are compared on the same flow. KiCad today; Altium, Fusion and Adom's own web apps next.", "summary": "Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board. The AI thinks its way from placement through routing, pours, current and thermal analysis to a delivered video; the binary does the fast, deterministic parts of every step, hands back hints, and keeps a ledger of every turn, every return to an earlier step, and the clock from the prompt to done.",
page.json+1−1@@ -1,7 +1,7 @@ { "slug": "adom-aiflow", "type": "app",- "version": "0.1.80",+ "version": "0.1.81", "title": "AI Flow", "description": "Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board. One Rust binary with a crate per step (placement helpers, a grid router with Kelvin taps, pours with keepouts, KiCad's DRC gate, live landing through the KiCad Bridge, copper measurement, current and thermal analysis) and a finish line that refuses an unfinished board. Every command answers with hints for the AI; every turn, its thinking time and every rework loop go into run.jsonl, so Claude, Codex and any other engine are compared on the same flow. KiCad today; Altium, Fusion and Adom's own web apps next.", "summary": "Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board. The AI thinks its way from placement through routing, pours, current and thermal analysis to a delivered video; the binary does the fast, deterministic parts of every step, hands back hints, and keeps a ledger of every turn, every return to an earlier step, and the clock from the prompt to done.",
skills/adom-aiflow/SKILL.md+6@@ -343,3 +343,9 @@ When a narrow sense or parallel branch causes a full-net width warning, read `do ### Unclipped Fields current maxima Current acceptance requires `peakJDefinition` declaring an unclipped, unrounded cell maximum with a copper-fraction cutoff no greater than 0.25. Earlier Fields reports clipped peaks for visualization and can hide current-density failures. Do not copy the new marker onto an old report: rerun a reviewed solver with the reporting fix. Owner review, release and installed availability remain separate; a pending source fix is not a qualified board. Keep the failed gate visible while correcting bottlenecks and continue other authorized work.++### Plated-hole current review (0.1.81)++A current report must identify distributed plated-wall coupling with removed drill voids and include every plated connection. AIFlow independently checks signed current divided by barrel area, compares it with the barrel-density limit, and rejects `current-barrel` findings even when surface current passes. Old center-cell reports can contain apparent surface hotspots inside drill holes; do not alter the PCB merely to chase that artifact or stamp new metadata onto cached output. The corrected Fields implementation is under owner review in adom/adom-fields PR 9 (dependencies 8, 6 and 5). Preserve shared runtime pins and continue other authorized work while an owner review is pending.++For native saved-board experiments, preserve a UUID-to-net map before and after connectivity build, refill, save and reload. KiCad can reassign a crossing copper item's net during connectivity repair; zero DRC errors alone do not prove the intended circuit. Reject changed intent, correct geometry and rerun. Keep the live board unchanged until the candidate's geometry, net assignments and engineering checks are reviewed.
skills/aiflow-measurement/SKILL.md+6@@ -69,3 +69,9 @@ When a narrow sense or parallel branch causes a full-net width warning, read `do ### Unclipped Fields current maxima Current acceptance requires `peakJDefinition` declaring an unclipped, unrounded cell maximum with a copper-fraction cutoff no greater than 0.25. Earlier Fields reports clipped peaks for visualization and can hide current-density failures. Do not copy the new marker onto an old report: rerun a reviewed solver with the reporting fix. Owner review, release and installed availability remain separate; a pending source fix is not a qualified board. Keep the failed gate visible while correcting bottlenecks and continue other authorized work.++### Plated-hole current review (0.1.81)++A current report must identify distributed plated-wall coupling with removed drill voids and include every plated connection. AIFlow independently checks signed current divided by barrel area, compares it with the barrel-density limit, and rejects `current-barrel` findings even when surface current passes. Old center-cell reports can contain apparent surface hotspots inside drill holes; do not alter the PCB merely to chase that artifact or stamp new metadata onto cached output. The corrected Fields implementation is under owner review in adom/adom-fields PR 9 (dependencies 8, 6 and 5). Preserve shared runtime pins and continue other authorized work while an owner review is pending.++For native saved-board experiments, preserve a UUID-to-net map before and after connectivity build, refill, save and reload. KiCad can reassign a crossing copper item's net during connectivity repair; zero DRC errors alone do not prove the intended circuit. Reject changed intent, correct geometry and rerun. Keep the live board unchanged until the candidate's geometry, net assignments and engineering checks are reviewed.