← Commit history

AI Flow 0.1.53: Fusion step clips framed on the board, small-board silkscreen text, adom-lbr dependency

John Lauer ·c50db37e22 ·4h ago ·parent 81097e8
9 files changed +48−16
Cargo.lock+10−10
@@ -4,7 +4,7 @@ version = 4  [[package]] name = "adom-aiflow"-version = "0.1.52"+version = "0.1.53" dependencies = [  "aiflow-analyze",  "aiflow-board",@@ -24,7 +24,7 @@ dependencies = [  [[package]] name = "aiflow-analyze"-version = "0.1.52"+version = "0.1.53" dependencies = [  "serde",  "serde_json",@@ -33,7 +33,7 @@ dependencies = [  [[package]] name = "aiflow-board"-version = "0.1.52"+version = "0.1.53" dependencies = [  "roxmltree",  "serde",@@ -42,7 +42,7 @@ dependencies = [  [[package]] name = "aiflow-bridge"-version = "0.1.52"+version = "0.1.53" dependencies = [  "aiflow-board",  "serde",@@ -51,7 +51,7 @@ dependencies = [  [[package]] name = "aiflow-copper"-version = "0.1.52"+version = "0.1.53" dependencies = [  "aiflow-board",  "aiflow-grid",@@ -61,7 +61,7 @@ dependencies = [  [[package]] name = "aiflow-grid"-version = "0.1.52"+version = "0.1.53" dependencies = [  "aiflow-board",  "serde",@@ -70,7 +70,7 @@ dependencies = [  [[package]] name = "aiflow-place"-version = "0.1.52"+version = "0.1.53" dependencies = [  "aiflow-board",  "serde",@@ -79,7 +79,7 @@ dependencies = [  [[package]] name = "aiflow-pours"-version = "0.1.52"+version = "0.1.53" dependencies = [  "aiflow-board",  "aiflow-copper",@@ -89,7 +89,7 @@ dependencies = [  [[package]] name = "aiflow-router"-version = "0.1.52"+version = "0.1.53" dependencies = [  "aiflow-board",  "aiflow-grid",@@ -99,7 +99,7 @@ dependencies = [  [[package]] name = "aiflow-run"-version = "0.1.52"+version = "0.1.53" dependencies = [  "serde",  "serde_json",
Cargo.toml+1−1
@@ -14,7 +14,7 @@ members = [ ]  [workspace.package]-version = "0.1.52"+version = "0.1.53" edition = "2021" license = "MIT" repository = "https://wiki.adom.inc/adom/adom-aiflow"
bin/adom-aiflow
⋯ 1 unchanged line ⋯
crates/adom-aiflow/src/main.rs+6−1
@@ -3666,7 +3666,12 @@ fn main() {                     clip_stopped = true;                 }                 // the fields step films the app's window through `tour fields`: no editor clip for it-                let hwnd = if name == "fields" { None } else { r.data["captures"].as_array().and_then(|c| c.iter().rev().find_map(|e| e.get("hwnd").and_then(|h| h.as_i64()))).or_else(|| br.pcb_editor_hwnd()) };+                // a Fusion run films Fusion's own window, the board fitted to it first (a remembered+                // hwnd can be a KiCad window from before the board, and Fusion keeps whatever zoom it had)+                let fusion_run = aiflow_bridge::fusion::active().is_some();+                let hwnd = if name == "fields" { None }+                    else if fusion_run { let h = br.pcb_editor_hwnd(); let _ = br.raw_call("fusion_view", &json!({"action": "fit", "space": "2d"})); h }+                    else { r.data["captures"].as_array().and_then(|c| c.iter().rev().find_map(|e| e.get("hwnd").and_then(|h| h.as_i64()))).or_else(|| br.pcb_editor_hwnd()) };                 let disk = disk_check(&dir, &br, name);                 if let Err(e) = &disk {                     r.log("clip-refused", json!({"step": name, "why": e}));
docs/fusion-lane.md+2
@@ -51,6 +51,8 @@ behind the flow's back is refused (`stale_board`), never edited blind. | `fusion reset` | A refine pass: every track, via and pour cleared from the live board; the parts stay. | | `fusion export` | Gerbers (the JLCPCB CAM job), pick and place, and the BOM with every distributor number, in `<run>/manufacturing/`. Ask the human first. | +On a Fusion run every step's clip films Fusion's main window, the board fitted to the view first. The library sets part names to 0.8 mm and puts values on tDocu, so the silkscreen stays readable on a small board.+ A failing `analyze` prints `why:` lines with the cause of each field-screen neck and the spec change that fixes it: a via whose copper ring is the narrowest copper (`viaPower`), or another net's track cutting a patch pour off from its pin (`planeUnder`).
docs/release-0.1.53.mdadded+5
@@ -0,0 +1,5 @@+# AI Flow 0.1.53++- **Step clips on a Fusion run film Fusion.** Every step's clip records Fusion's own window, with the board fitted to the view first. Before, a clip could point at a KiCad window left from before the board, and Fusion kept whatever zoom it had, so the board showed as a speck.+- **Silkscreen text a small board can carry.** `fusion library` sets each part's name to 0.8 mm text and moves its value to the documentation layer (tDocu), which is drawn in the editor but never printed. Fusion's 1.27 mm default overlapped across a 28 x 20 mm board.+- **adom-lbr is a declared dependency.** `fusion library` checks for `adom-lbr` and `adom-wiki` up front and names the install command, instead of failing partway.
package.json+5−3
@@ -1,7 +1,7 @@ {   "slug": "adom-aiflow",   "type": "app",-  "version": "0.1.52",+  "version": "0.1.53",   "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.",@@ -44,7 +44,9 @@     "run.jsonl",     "thermal analysis"   ],-  "dependencies": {},+  "dependencies": {+    "adom/adom-lbr": "*"+  },   "scripts": {     "install": "./install.sh",     "uninstall": "./uninstall.sh"@@ -83,4 +85,4 @@   "confirmed_user_skills": [     "aiflow-simulate"   ]-}+}\ No newline at end of file
page.json+1−1
@@ -1,7 +1,7 @@ {   "slug": "adom-aiflow",   "type": "app",-  "version": "0.1.52",+  "version": "0.1.53",   "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.",
tools/fusion-project.py+18
@@ -213,7 +213,24 @@ def symbol_choice(comp, fp_text, sym_path):     return 'own'  +def silk_text(t):+    """Board text a small board can carry: each package's >NAME at 0.8 mm (thick enough for a fab's+    0.15 mm line), and its >VALUE on tDocu (51), drawn in the editor but never printed. Fusion copies+    these onto every element when the part is added, so the library is where they are set."""+    def fix(m):+        tag, label = m.group(1), m.group(2)+        tag = re.sub(r'size="[^"]*"', 'size="0.8"', tag)+        tag = tag if 'ratio=' in tag else tag.replace('<text ', '<text ratio="15" ', 1)+        if label == 'VALUE': tag = re.sub(r'layer="27"', 'layer="51"', tag)+        return tag + '&gt;' + label+    def pkg(m):+        return re.sub(r'(<text [^>]*layer="2[57]"[^>]*>)&gt;(NAME|VALUE)', fix, m.group(0))+    return re.sub(r'<package [^>]*>.*?</package>', pkg, t, flags=re.S)++ def cmd_library(a):+    for tool, pkg in (('adom-lbr', 'adom/adom-lbr'), ('adom-wiki', 'adom/adom-wiki')):+        if not shutil.which(tool): err('%s is not installed (it turns each part page into Fusion library parts)' % tool, ['adom-wiki pkg install %s, then run this again.' % pkg])     st = load_state(); name = st.get('library') or a.name or 'Project'     if not re.fullmatch(r'[A-Za-z0-9_-]{1,40}', name): err('library name: letters, digits, _ and - only')     st['library'] = name@@ -293,6 +310,7 @@ def cmd_library(a):     r = run(['adom-lbr', 'assemble', *lbrs, '-o', str(combined), '--name', name], check=True)     t = combined.read_text()     t = re.sub(r'<deviceset ((?:(?!uservalue)[^>])*)>', lambda m: '<deviceset %s uservalue="yes">' % m.group(1), t)+    t = silk_text(t)     combined.write_text(t)     # 3D: build only the parts that have no binding yet, then carry every binding into the library     todo = [m for m, p in st['parts'].items() if p.get('step') and not p.get('urn')]