← Commit history
SKILL.mdadded+51
@@ -0,0 +1,51 @@+---+name: adom-aiflow+description: The board loop as one command for KiCad boards on the Adom KiCad Bridge: start a measured run from the human's prompt, plan the stages and say which ones you take yourself (placement, routing) and which the binary runs (router, pours, gate, landing, measurement, current and thermal analysis), then finish only when the board is 100 percent routed, DRC-clean, poured, analysed and measured. One clock from the prompt to the finish line and one manifest per run, so Claude, Codex and any other engine compare on the same flow. Every command answers OK or ERROR with Hint lines that depend on this board and this run. Trigger words: aiflow, adom-aiflow, place and route the board, route the board to 100%, copper pours, current density, thermal analysis, finish the board, compare engines on a board, board loop, from placement to fab, kelvin keepouts, ablation copper, measured run, prompt-to-board time.+---++# adom-aiflow: the loop, measured++This tool is the place-route-pour-analyze skill made executable: the step order is a file, the gates refuse, the hints arrive as the answer to the command you just ran, and the ledger records what you did. Follow the commands and the skill follows itself.++Install: `adom-wiki pkg install adom/adom-aiflow` (one Rust binary; `service-kicad` for the offline DRC gate, and the KiCad Bridge on a desktop for the live stages). `adom-aiflow --version`.++Every state-changing command takes `--ai-thread "<your thread name>"`. Every run lives in one directory (`--run DIR`, default `./aiflow-run`) with a `run.json` manifest: the clock, the plan, the decisions, every stage's start and end, the outcomes, the capture markers and the summary. The manifest is the comparison; nothing counts that is not in it.++## The flow++The flow is a file, `flows/board.json`: the steps in order (intake, placement, routing, pours, current, thermal, capture, finish), who does each, what the binary offers at each, and the steps that come later (components, libraries, schematic, simulation before the board; moleculize, paste, probe after it). For now the flow starts at parts placement and ends at a delivered, analysed board. `plan` prints it.++1. **`start --board B.kicad_pcb --spec spec.json --engine <you> --prompt-time <UTC of the paste> [--target <box> --remote-board <path on the box>]`**: copies the board into the run and starts the clock at the prompt, not at your first command. Record the paste time honestly.+2. **`plan`**: the stages this board needs and who can take each. Placement is yours (the binary helps: `place pack --wish`, `place check --moves`, `place land --moves`). Routing is the binary's grid router by default; say `take route=ai` if you route yourself. Pours, gate, landing, measurement, both analyses and the finish are the binary's.+3. **`stage start <name>` / `stage end <name>`** around any stage you do yourself, so the clock and the manifest see it.+4. **`route [--passes N]`**: the grid router on the current board with the spec's rules (wide nets, planes, per-net vias, Kelvin taps). If it closes short, the ERROR names the pins and the blockers and the three levers: more passes, back to placement (`place pack` the parts it names or the test points and indicators in the escape band, `place check`, route again), or take the stage yourself.+5. **`gate`**: KiCad's DRC offline on the plan, inherited errors separated. Nothing lands until it passes.+6. **`land route`**, then **`pour`**, **`land vias`**, **`land pours`**: live through the bridge as native undo steps, each DRC-checked before it lands. A refused pour's ERROR says why (starved thermal reliefs, intersecting zones, clearance) and what to change in the spec. Both landings converge the live board on the plan, so you change the spec, run `pour` and land again: a via already at its planned place is not landed twice, a zone with its planned name and outline is kept, and every zone the flow landed that the plan no longer describes (renamed, reshaped, dropped) is removed first. Zones the board started with are never touched.+7. **`measure`**: KiCad's filled copper per layer, the ablation metric. **`analyze current`** and **`analyze thermal`**: IPC-2221 on the narrowest conductor of every loaded net, pour and via capacity, copper and vias at every hot tab. A loaded net whose pads all sit on one poured outer layer is carried by that layer's pour; its vias only spread heat. Hot parts take a theta ceiling (`maxThetaCPerW`) or a rise budget (`hotMaxRiseC` spec-wide, `maxRiseC` per part) against the stated watts, so write the dissipation physically (I2R at the hot Rds(on) over the conduction duty, plus switching) and say so in the spec. A FAIL names the net or part and the fix; fix it (pours, vias, widths, or placement), land, measure and analyze again.+8. **`finish`**, then **`deliver --video <mp4> --message "..."`**: `finish` refuses until the gate passed, routing and pours landed, copper measured, both analyses passed, and the live board validates at 0 unconnected with no new errors. Then it stamps the end and writes the summary: minutes from the prompt, minutes per stage, decisions, copper, live DRC. Add your token and dollar accounting to the manifest under `tokens` and `usd`.++## Capture, so the engines' videos line up++One clip per step. `capture open` puts the board on the test box; from then on every `step <name>` stops the previous step's clip and starts this step's own window recording, tagged with the step, and its hint says what that clip should show (the flow file's `record` line: the parts landing for placement, the nets landing for routing, the pours filling for pours, the return to an earlier step when an analysis fails). `deliver` lists the clips. The final video is cut from them, one segment per step, so two engines' videos line up step for step, and the page can show a little clip beside every step's numbers.++`capture open` puts the board on screen the same way for every engine (foreground OpenGL on a test box we own, maximised, zoom to fit). `capture start` records the PCB editor window itself through Adom Bridge's window recorder (Windows Graphics Capture, background-capturable: whatever another thread or an updater puts in front of KiCad on a shared box does not reach the take, and nobody at the box is disturbed; the monitor is recorded only when no editor is open), every stage command writes a marker with its timestamp into the manifest, `capture mark <label>` adds yours, `capture stop` pulls the MP4 into the run. The split-screen comparison (gang-takes split mode) aligns the two engines on those markers.++## The spec++`docs/spec-example.json` on the page is the ESC G431's: copper thickness, clearances, the inherited error count, fixed refs (the molecule interface), planes, wide and mid nets, Kelvin pairs, loads per net (amps, max rise), hot parts (watts, tab net), the pours (outline, around parts with a margin and exclusions, or explicit polygons; priorities and connection styles), solid patches, thermal and stitching vias, and the nets that are deliberately not poured. Write the spec from the schematic before you start; it is the electrical judgement, and it is what makes two engines' runs comparable.++## The rules behind the stages++The judgement is in these skills, which this tool executes: kicad-place-route-loop (place for routability, the levers when routing cannot close, go back to placement), kicad-copper-pours (which nets get a pour and which never do, priorities, the Kelvin pair, the preflight lessons), and the tool-neutral eda-engineering skillpack (eda-end-to-end-layout, eda-kelvin-current-sense, eda-copper-ablation, eda-pour-planning-measurement, eda-thermal-bottlenecks, eda-multilayer-current-review).++## What is measured (read aiflow-measurement)++The number is the human's prompt to your "done, here is your video" (`deliver`), and the cost per step is your thinking, not the binary's seconds. Every command you run is a turn in the append-only run.jsonl with the gap before it as thinking time (`adom-aiflow ledger` reads it back); declare `step <name>` as you move, and `step <name> --back --why "..."` when a later step sends you back. `finish` is the qualified board; `deliver --video --message` ends the clock.++## Honesty rules++- The clock starts at the prompt. A late `start` takes `--prompt-time`.+- A stage you take yourself is stamped by you and named in the manifest as `ai`; the binary's stages are `binary`. Both go on the chart.+- `finish` is the only thing that ends a run. A run without `finish` is not a result, and its minutes are still counting.+- KiCad drops pour islands that touch nothing: a heat spreader drawn across dense routing on the other layer fills as fragments and reads small in `measure`. Put the copper where the layer is actually free (the analysis numbers say when it is not).+- 0.1's analyses are conservative heuristics (IPC-2221 for tracks, presence, connection style and vias for pours and tabs); they say so in their output. 0.2 computes cross-sections through the filled copper.
bin/adom-aiflowadded
⋯ 1 unchanged line ⋯
docs/flow-hero.pngadded
⋯ 1 unchanged line ⋯
docs/flow-hero.svgadded+76
@@ -0,0 +1,76 @@+<svg xmlns="http://www.w3.org/2000/svg" width="806" height="892" viewBox="0 0 806 892" font-family="Familjen Grotesk, Satoshi, Inter, Segoe UI, sans-serif">+<defs><marker id="fwd" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" fill="#DB8B58"/></marker><marker id="back" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" fill="#7FD6D1"/></marker><marker id="to" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" fill="#00B8B1"/></marker></defs>+<rect width="806" height="892" fill="#0B1420"/>+<text x="255" y="44" fill="#DB8B58" font-size="11.5" font-weight="700" letter-spacing="2" text-anchor="middle">THE AI: THINKS, DECIDES, GOES BACK</text>+<text x="570" y="44" fill="#00B8B1" font-size="11.5" font-weight="700" letter-spacing="2" text-anchor="middle">THE BINARY: THE FAST PARTS</text>+<text x="22" y="437" fill="#7FD6D1" font-size="11.5" font-style="italic" transform="rotate(-90 22 437)" text-anchor="middle">rework, as often as it needs</text>+<path d="M150 215 C 106 215, 106 117, 150 117" fill="none" stroke="#7FD6D1" stroke-opacity=".85" stroke-width="2" stroke-dasharray="5 6" marker-end="url(#back)"/>+<path d="M150 313 C 76 313, 76 117, 150 117" fill="none" stroke="#7FD6D1" stroke-opacity=".85" stroke-width="2" stroke-dasharray="5 6" marker-end="url(#back)"/>+<path d="M150 411 C 98 411, 98 215, 150 215" fill="none" stroke="#7FD6D1" stroke-opacity=".85" stroke-width="2" stroke-dasharray="5 6" marker-end="url(#back)"/>+<path d="M150 509 C 106 509, 106 313, 150 313" fill="none" stroke="#7FD6D1" stroke-opacity=".85" stroke-width="2" stroke-dasharray="5 6" marker-end="url(#back)"/>+<path d="M150 509 C 52 509, 52 117, 150 117" fill="none" stroke="#7FD6D1" stroke-opacity=".85" stroke-width="2" stroke-dasharray="5 6" marker-end="url(#back)"/>+<rect x="150" y="84" width="210" height="66" rx="9" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="166" y="113" fill="#F2E9E1" font-size="25" font-weight="700">Place</text>+<text x="166" y="135" fill="#B9A899" font-size="12">parts for routability, current, heat</text>+<line x1="255.0" y1="152" x2="255.0" y2="180" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<line x1="366" y1="111" x2="392" y2="111" stroke="#93A3B4" stroke-width="1.3" marker-end="url(#to)"/>+<line x1="392" y1="125" x2="366" y2="125" stroke="#00B8B1" stroke-width="1.3" marker-end="url(#to)"/>+<rect x="400" y="93" width="340" height="48" rx="5" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.4"/>+<text x="414" y="123" fill="#DDF7F5" font-size="15" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace">place pack · check · land moves</text>+<rect x="150" y="182" width="210" height="66" rx="9" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="166" y="211" fill="#F2E9E1" font-size="25" font-weight="700">Route</text>+<text x="166" y="233" fill="#B9A899" font-size="12">100 % or it failed</text>+<line x1="255.0" y1="250" x2="255.0" y2="278" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<line x1="366" y1="209" x2="392" y2="209" stroke="#93A3B4" stroke-width="1.3" marker-end="url(#to)"/>+<line x1="392" y1="223" x2="366" y2="223" stroke="#00B8B1" stroke-width="1.3" marker-end="url(#to)"/>+<rect x="400" y="191" width="340" height="48" rx="5" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.4"/>+<text x="414" y="221" fill="#DDF7F5" font-size="15" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace">route · gate · land</text>+<rect x="150" y="280" width="210" height="66" rx="9" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="166" y="309" fill="#F2E9E1" font-size="25" font-weight="700">Pour</text>+<text x="166" y="331" fill="#B9A899" font-size="12">copper kept, Kelvin keepouts</text>+<line x1="255.0" y1="348" x2="255.0" y2="376" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<line x1="366" y1="307" x2="392" y2="307" stroke="#93A3B4" stroke-width="1.3" marker-end="url(#to)"/>+<line x1="392" y1="321" x2="366" y2="321" stroke="#00B8B1" stroke-width="1.3" marker-end="url(#to)"/>+<rect x="400" y="289" width="340" height="48" rx="5" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.4"/>+<text x="414" y="319" fill="#DDF7F5" font-size="15" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace">pour · land pours · measure</text>+<rect x="150" y="378" width="210" height="66" rx="9" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="166" y="407" fill="#F2E9E1" font-size="25" font-weight="700">Current</text>+<text x="166" y="429" fill="#B9A899" font-size="12">IPC-2221, vias, pours</text>+<line x1="255.0" y1="446" x2="255.0" y2="474" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<line x1="366" y1="405" x2="392" y2="405" stroke="#93A3B4" stroke-width="1.3" marker-end="url(#to)"/>+<line x1="392" y1="419" x2="366" y2="419" stroke="#00B8B1" stroke-width="1.3" marker-end="url(#to)"/>+<rect x="400" y="387" width="340" height="48" rx="5" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.4"/>+<text x="414" y="417" fill="#DDF7F5" font-size="15" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace">analyze current</text>+<rect x="150" y="476" width="210" height="66" rx="9" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="166" y="505" fill="#F2E9E1" font-size="25" font-weight="700">Thermal</text>+<text x="166" y="527" fill="#B9A899" font-size="12">copper at every hot tab</text>+<line x1="255.0" y1="544" x2="255.0" y2="572" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<line x1="366" y1="503" x2="392" y2="503" stroke="#93A3B4" stroke-width="1.3" marker-end="url(#to)"/>+<line x1="392" y1="517" x2="366" y2="517" stroke="#00B8B1" stroke-width="1.3" marker-end="url(#to)"/>+<rect x="400" y="485" width="340" height="48" rx="5" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.4"/>+<text x="414" y="515" fill="#DDF7F5" font-size="15" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace">analyze thermal</text>+<rect x="150" y="574" width="210" height="66" rx="9" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="166" y="603" fill="#F2E9E1" font-size="25" font-weight="700">Capture</text>+<text x="166" y="625" fill="#B9A899" font-size="12">one clip per step</text>+<line x1="255.0" y1="642" x2="255.0" y2="670" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<line x1="366" y1="601" x2="392" y2="601" stroke="#93A3B4" stroke-width="1.3" marker-end="url(#to)"/>+<line x1="392" y1="615" x2="366" y2="615" stroke="#00B8B1" stroke-width="1.3" marker-end="url(#to)"/>+<rect x="400" y="583" width="340" height="48" rx="5" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.4"/>+<text x="414" y="613" fill="#DDF7F5" font-size="15" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace">capture</text>+<rect x="150" y="672" width="210" height="66" rx="9" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="166" y="701" fill="#F2E9E1" font-size="25" font-weight="700">Finish</text>+<text x="166" y="723" fill="#B9A899" font-size="12">refuses until it is all true</text>+<line x1="255.0" y1="740" x2="255.0" y2="768" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<line x1="366" y1="699" x2="392" y2="699" stroke="#93A3B4" stroke-width="1.3" marker-end="url(#to)"/>+<line x1="392" y1="713" x2="366" y2="713" stroke="#00B8B1" stroke-width="1.3" marker-end="url(#to)"/>+<rect x="400" y="681" width="340" height="48" rx="5" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.4"/>+<text x="414" y="711" fill="#DDF7F5" font-size="15" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace">finish</text>+<rect x="150" y="770" width="210" height="66" rx="9" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="166" y="799" fill="#F2E9E1" font-size="25" font-weight="700">Deliver</text>+<text x="166" y="821" fill="#B9A899" font-size="12">"done, here is your video"</text>+<line x1="366" y1="797" x2="392" y2="797" stroke="#93A3B4" stroke-width="1.3" marker-end="url(#to)"/>+<line x1="392" y1="811" x2="366" y2="811" stroke="#00B8B1" stroke-width="1.3" marker-end="url(#to)"/>+<rect x="400" y="779" width="340" height="48" rx="5" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.4"/>+<text x="414" y="809" fill="#DDF7F5" font-size="15" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace">deliver · run.jsonl</text>+<text x="380" y="876" fill="#93A3B4" font-size="11.5" text-anchor="middle">down: specs, configs, plans, scripts  ·  up: OK / ERROR + hints  ·  any box can be the AI's own code</text>+</svg>\ No newline at end of file
docs/flow-preview.pngadded
⋯ 1 unchanged line ⋯
docs/flow.svgadded+118
@@ -0,0 +1,118 @@+<svg xmlns="http://www.w3.org/2000/svg" width="1520" height="900" viewBox="0 0 1520 900" font-family="Familjen Grotesk, Satoshi, Inter, Segoe UI, sans-serif">+<defs><marker id="fwd" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" fill="#DB8B58"/></marker><marker id="back" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" fill="#7FD6D1"/></marker><marker id="down" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" fill="#93A3B4"/></marker><marker id="up" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" fill="#00B8B1"/></marker></defs>+<rect width="1520" height="900" fill="#0B1420"/>+<text x="40" y="178" fill="#DB8B58" font-size="17" font-weight="700" letter-spacing="1.5">THE AI</text>+<text x="40" y="200" fill="#DB8B58" font-size="17" font-weight="700" letter-spacing="1.5">drives, thinks,</text>+<text x="40" y="222" fill="#DB8B58" font-size="17" font-weight="700" letter-spacing="1.5">goes back</text>+<text x="40" y="538" fill="#00B8B1" font-size="17" font-weight="700" letter-spacing="1.5">THE BINARY</text>+<text x="40" y="560" fill="#00B8B1" font-size="17" font-weight="700" letter-spacing="1.5">fast, deterministic,</text>+<text x="40" y="582" fill="#00B8B1" font-size="17" font-weight="700" letter-spacing="1.5">never reinvented</text>+<path d="M601 158.0 C 601 100.0, 444 100.0, 444 158.0" fill="none" stroke="#7FD6D1" stroke-opacity=".8" stroke-width="2.2" stroke-dasharray="6 7" marker-end="url(#back)"/>+<path d="M759 158.0 C 759 62.0, 444 62.0, 444 158.0" fill="none" stroke="#7FD6D1" stroke-opacity=".8" stroke-width="2.2" stroke-dasharray="6 7" marker-end="url(#back)"/>+<path d="M916 158.0 C 916 88.0, 601 88.0, 601 158.0" fill="none" stroke="#7FD6D1" stroke-opacity=".8" stroke-width="2.2" stroke-dasharray="6 7" marker-end="url(#back)"/>+<path d="M1074 158.0 C 1074 100.0, 759 100.0, 759 158.0" fill="none" stroke="#7FD6D1" stroke-opacity=".8" stroke-width="2.2" stroke-dasharray="6 7" marker-end="url(#back)"/>+<path d="M1074 158.0 C 1074 34.0, 444 34.0, 444 158.0" fill="none" stroke="#7FD6D1" stroke-opacity=".8" stroke-width="2.2" stroke-dasharray="6 7" marker-end="url(#back)"/>+<path d="M1074 158.0 C 1074 64.0, 601 64.0, 601 158.0" fill="none" stroke="#7FD6D1" stroke-opacity=".8" stroke-width="2.2" stroke-dasharray="6 7" marker-end="url(#back)"/>+<text x="838" y="22.0" fill="#7FD6D1" font-size="16" text-anchor="middle" font-style="italic">rework, as often as it needs: a bad pour sends it back to placement, a hot tab back to the pours or the routing</text>+<rect x="215" y="158.0" width="142" height="84" rx="10" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="286" y="188" fill="#F2E9E1" font-size="24" font-weight="700" text-anchor="middle">Spec</text>+<text x="286" y="212" fill="#B9A899" font-size="12.5" text-anchor="middle">schematic's</text>+<text x="286" y="228" fill="#B9A899" font-size="12.5" text-anchor="middle">judgement</text>+<line x1="357" y1="200" x2="371" y2="200" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<rect x="373" y="158.0" width="142" height="84" rx="10" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="444" y="188" fill="#F2E9E1" font-size="24" font-weight="700" text-anchor="middle">Place</text>+<text x="444" y="212" fill="#B9A899" font-size="12.5" text-anchor="middle">parts, courtyards,</text>+<text x="444" y="228" fill="#B9A899" font-size="12.5" text-anchor="middle">escape bands</text>+<line x1="515" y1="200" x2="528" y2="200" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<rect x="530" y="158.0" width="142" height="84" rx="10" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="601" y="188" fill="#F2E9E1" font-size="24" font-weight="700" text-anchor="middle">Route</text>+<text x="601" y="212" fill="#B9A899" font-size="12.5" text-anchor="middle">100 % or it</text>+<text x="601" y="228" fill="#B9A899" font-size="12.5" text-anchor="middle">failed</text>+<line x1="672" y1="200" x2="686" y2="200" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<rect x="688" y="158.0" width="142" height="84" rx="10" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="759" y="188" fill="#F2E9E1" font-size="24" font-weight="700" text-anchor="middle">Pour</text>+<text x="759" y="212" fill="#B9A899" font-size="12.5" text-anchor="middle">copper kept,</text>+<text x="759" y="228" fill="#B9A899" font-size="12.5" text-anchor="middle">Kelvin keepouts</text>+<line x1="830" y1="200" x2="843" y2="200" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<rect x="845" y="158.0" width="142" height="84" rx="10" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="916" y="188" fill="#F2E9E1" font-size="24" font-weight="700" text-anchor="middle">Current</text>+<text x="916" y="212" fill="#B9A899" font-size="12.5" text-anchor="middle">IPC-2221,</text>+<text x="916" y="228" fill="#B9A899" font-size="12.5" text-anchor="middle">vias, pours</text>+<line x1="987" y1="200" x2="1001" y2="200" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<rect x="1003" y="158.0" width="142" height="84" rx="10" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="1074" y="188" fill="#F2E9E1" font-size="24" font-weight="700" text-anchor="middle">Thermal</text>+<text x="1074" y="212" fill="#B9A899" font-size="12.5" text-anchor="middle">copper at</text>+<text x="1074" y="228" fill="#B9A899" font-size="12.5" text-anchor="middle">every hot tab</text>+<line x1="1145" y1="200" x2="1158" y2="200" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<rect x="1160" y="158.0" width="142" height="84" rx="10" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="1231" y="188" fill="#F2E9E1" font-size="24" font-weight="700" text-anchor="middle">Capture</text>+<text x="1231" y="212" fill="#B9A899" font-size="12.5" text-anchor="middle">window take,</text>+<text x="1231" y="228" fill="#B9A899" font-size="12.5" text-anchor="middle">markers</text>+<line x1="1302" y1="200" x2="1316" y2="200" stroke="#DB8B58" stroke-width="2.5" marker-end="url(#fwd)"/>+<rect x="1318" y="158.0" width="142" height="84" rx="10" fill="#17283F" stroke="#DB8B58" stroke-width="2"/>+<text x="1389" y="188" fill="#F2E9E1" font-size="24" font-weight="700" text-anchor="middle">Deliver</text>+<text x="1389" y="212" fill="#B9A899" font-size="12.5" text-anchor="middle">"done, here is</text>+<text x="1389" y="228" fill="#B9A899" font-size="12.5" text-anchor="middle">your video"</text>+<line x1="272" y1="248.0" x2="272" y2="502.0" stroke="#93A3B4" stroke-width="1.6" marker-end="url(#down)"/>+<line x1="300" y1="504.0" x2="300" y2="250.0" stroke="#00B8B1" stroke-width="1.6" marker-end="url(#up)"/>+<line x1="430" y1="248.0" x2="430" y2="502.0" stroke="#93A3B4" stroke-width="1.6" marker-end="url(#down)"/>+<line x1="458" y1="504.0" x2="458" y2="250.0" stroke="#00B8B1" stroke-width="1.6" marker-end="url(#up)"/>+<line x1="587" y1="248.0" x2="587" y2="502.0" stroke="#93A3B4" stroke-width="1.6" marker-end="url(#down)"/>+<line x1="615" y1="504.0" x2="615" y2="250.0" stroke="#00B8B1" stroke-width="1.6" marker-end="url(#up)"/>+<line x1="745" y1="248.0" x2="745" y2="502.0" stroke="#93A3B4" stroke-width="1.6" marker-end="url(#down)"/>+<line x1="773" y1="504.0" x2="773" y2="250.0" stroke="#00B8B1" stroke-width="1.6" marker-end="url(#up)"/>+<line x1="902" y1="248.0" x2="902" y2="502.0" stroke="#93A3B4" stroke-width="1.6" marker-end="url(#down)"/>+<line x1="930" y1="504.0" x2="930" y2="250.0" stroke="#00B8B1" stroke-width="1.6" marker-end="url(#up)"/>+<line x1="1060" y1="248.0" x2="1060" y2="502.0" stroke="#93A3B4" stroke-width="1.6" marker-end="url(#down)"/>+<line x1="1088" y1="504.0" x2="1088" y2="250.0" stroke="#00B8B1" stroke-width="1.6" marker-end="url(#up)"/>+<line x1="1217" y1="248.0" x2="1217" y2="502.0" stroke="#93A3B4" stroke-width="1.6" marker-end="url(#down)"/>+<line x1="1245" y1="504.0" x2="1245" y2="250.0" stroke="#00B8B1" stroke-width="1.6" marker-end="url(#up)"/>+<line x1="1375" y1="248.0" x2="1375" y2="502.0" stroke="#93A3B4" stroke-width="1.6" marker-end="url(#down)"/>+<line x1="1403" y1="504.0" x2="1403" y2="250.0" stroke="#00B8B1" stroke-width="1.6" marker-end="url(#up)"/>+<rect x="492" y="344.0" width="690" height="64" rx="8" fill="#0B1420"/>+<text x="838" y="369.0" fill="#93A3B4" font-size="15" text-anchor="middle">down: specs, configs, plans, scripts, as many as it wants, in JSON it can write itself</text>+<text x="838" y="394.0" fill="#00B8B1" font-size="15" text-anchor="middle">up: OK or ERROR, with hints about this board, this run, this step</text>+<rect x="210" y="510.0" width="152" height="100" rx="6" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.6"/>+<text x="286" y="532.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">spec.json</text>+<text x="286" y="588.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">the rules, the loads,</text>+<text x="286" y="602.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">the pours to make</text>+<rect x="368" y="510.0" width="152" height="100" rx="6" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.6"/>+<text x="444" y="532.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">place pack</text>+<text x="444" y="548.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">place check</text>+<text x="444" y="564.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">land moves</text>+<text x="444" y="588.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">courtyards, wishes,</text>+<text x="444" y="602.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">radii, keepouts</text>+<rect x="525" y="510.0" width="152" height="100" rx="6" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.6"/>+<text x="601" y="532.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">route · gate</text>+<text x="601" y="548.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">land route</text>+<text x="601" y="588.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">grid router, rip-up,</text>+<text x="601" y="602.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">Kelvin taps, DRC gate</text>+<rect x="683" y="510.0" width="152" height="100" rx="6" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.6"/>+<text x="759" y="532.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">pour · land pours</text>+<text x="759" y="548.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">measure</text>+<text x="759" y="588.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">zones, keepouts, vias,</text>+<text x="759" y="602.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">KiCad's filled copper</text>+<rect x="840" y="510.0" width="152" height="100" rx="6" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.6"/>+<text x="916" y="532.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">analyze current</text>+<text x="916" y="588.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">narrowest conductor,</text>+<text x="916" y="602.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">via capacity</text>+<rect x="998" y="510.0" width="152" height="100" rx="6" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.6"/>+<text x="1074" y="532.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">analyze thermal</text>+<text x="1074" y="588.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">theta at the tab,</text>+<text x="1074" y="602.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">rise budget</text>+<rect x="1155" y="510.0" width="152" height="100" rx="6" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.6"/>+<text x="1231" y="532.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">capture</text>+<text x="1231" y="588.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">recorder,</text>+<text x="1231" y="602.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">markers</text>+<rect x="1313" y="510.0" width="152" height="100" rx="6" fill="#0F1D2E" stroke="#00B8B1" stroke-width="1.6"/>+<text x="1389" y="532.0" fill="#DDF7F5" font-size="13.5" font-weight="700" font-family="IBM Plex Mono, Consolas, monospace" text-anchor="middle">finish · deliver</text>+<text x="1389" y="588.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">ledger, summary,</text>+<text x="1389" y="602.0" fill="#8FB8B5" font-size="11.5" text-anchor="middle">the clock stops</text>+<rect x="210" y="660.0" width="1255" height="96" rx="10" fill="none" stroke="#93A3B4" stroke-width="1.6" stroke-dasharray="8 7"/>+<text x="838" y="692.0" fill="#E8EDF2" font-size="19" font-weight="700" text-anchor="middle">Any box can be the AI's own code instead.</text>+<text x="838" y="718.0" fill="#93A3B4" font-size="15" text-anchor="middle">Too rigid, not good enough? The AI takes the step itself, writes what it needs, and drops the same JSON back into the flow.</text>+<text x="838" y="740.0" fill="#93A3B4" font-size="15" text-anchor="middle">The binary is there so it does not reinvent the fast parts every run. The ledger measures it either way.</text>+<rect x="210" y="770.0" width="1255" height="52" rx="6" fill="#17283F"/>+<text x="838" y="803.0" fill="#F2E9E1" font-size="16" text-anchor="middle"><tspan font-family="IBM Plex Mono, Consolas, monospace" fill="#DB8B58" font-weight="700">run.jsonl</tspan>  ·  every turn, its thinking time and its tool time  ·  every return to an earlier step, and why  ·  one clock from the prompt to "done"</text>+<text x="760" y="878" fill="#5F7286" font-size="13.5" text-anchor="middle" letter-spacing="1">KiCad Bridge today  ·  Altium and Fusion bridges next  ·  then Adom's own web apps: adom-schematic, adom-2dboard, adom-3dboard</text>+</svg>\ No newline at end of file
docs/spec-example.jsonadded+553
@@ -0,0 +1,553 @@+{+  "board": "ESC G431 (Adom molecule: STM32G431, DRV8300, six BSC016N06NS, TPSM365 buck, INA181 current sense), 64 x 74 mm, 4 layers",+  "fab": "InstaPCB (copper ablation: keep as much copper as the electrical rules allow)",+  "copperUm": 35,+  "clearance": 0.2,+  "edgeClearance": 0.5,+  "inheritedErrors": 13,+  "inheritedErrorsNote": "six malformed MOSFET courtyards and seven U2 pad clearances come from the source libraries; they are counted separately and never fixed by the flow",+  "fixedRefs": [+    "MP1",+    "MP2",+    "MP3",+    "MP4",+    "MC1",+    "MC2",+    "MC3",+    "MC4",+    "MC5",+    "MC6",+    "MC7",+    "MC8",+    "MC9",+    "MC10",+    "MC11",+    "MC12",+    "MC13",+    "MC14",+    "MC15",+    "MC16",+    "MC17",+    "MC18",+    "MC19",+    "MC20",+    "MC21",+    "MC22",+    "MC23"+  ],+  "planes": {+    "GND": "In1.Cu",+    "+3V3": "In2.Cu"+  },+  "wideNets": {+    "+VBAT": [+      1.0,+      0.5,+      0.25+    ],+    "/DRV_SHA": [+      1.0,+      0.5,+      0.25+    ],+    "/DRV_SHB": [+      1.0,+      0.5,+      0.25+    ],+    "/DRV_SHC": [+      1.0,+      0.5,+      0.25+    ],+    "GND_OUT": [+      1.0,+      0.5,+      0.25+    ]+  },+  "midNets": {+    "+5V": [+      0.5,+      0.25+    ],+    "+12V": [+      0.5,+      0.25+    ]+  },+  "kelvin": {+    "U6.3": "R32.2",+    "U6.4": "R32.1"+  },+  "kelvinNote": "INA181 IN- (pad 3) taps the shunt's GND pad, IN+ (pad 4) taps its GND_OUT pad; dedicated traces, no plane stub, pour keepouts along them",+  "loads": {+    "+VBAT": {+      "amps": 20,+      "maxRiseC": 10+    },+    "/DRV_SHA": {+      "amps": 20,+      "maxRiseC": 10+    },+    "/DRV_SHB": {+      "amps": 20,+      "maxRiseC": 10+    },+    "/DRV_SHC": {+      "amps": 20,+      "maxRiseC": 10+    },+    "GND_OUT": {+      "amps": 20,+      "maxRiseC": 10+    },+    "+12V": {+      "amps": 0.5+    },+    "+5V": {+      "amps": 0.5+    }+  },+  "hot": {+    "Q1": {+      "watts": 0.6,+      "tabNet": "+VBAT"+    },+    "Q3": {+      "watts": 0.6,+      "tabNet": "+VBAT"+    },+    "Q5": {+      "watts": 0.6,+      "tabNet": "+VBAT"+    },+    "Q2": {+      "watts": 0.6,+      "tabNet": "/DRV_SHA"+    },+    "Q4": {+      "watts": 0.6,+      "tabNet": "/DRV_SHB"+    },+    "Q6": {+      "watts": 0.6,+      "tabNet": "/DRV_SHC"+    },+    "U1": {+      "watts": 0.5,+      "tabNet": "+VBAT",+      "note": "SOT-89 pad 2 (the tab) is +VBAT, the input; the +VBAT F.Cu block has a tongue down to it"+    }+  },+  "pours": [+    {+      "net": "GND",+      "outline": true,+      "layers": [+        "F.Cu",+        "B.Cu"+      ],+      "priority": 0,+      "connection": "thermal",+      "thermalGap": 0.3,+      "thermalBridge": 0.35,+      "name": "GND outer pour (ablation: keep the copper)"+    },+    {+      "net": "+VBAT",+      "polygon": [+        [+          127.58,+          59.8+        ],+        [+          159.13,+          59.8+        ],+        [+          159.13,+          90.9+        ],+        [+          140.0,+          90.9+        ],+        [+          140.0,+          93.4+        ],+        [+          131.0,+          93.4+        ],+        [+          131.0,+          90.9+        ],+        [+          127.58,+          90.9+        ]+      ],+      "layers": [+        "F.Cu"+      ],+      "priority": 2,+      "connection": "solid",+      "name": "+VBAT rail block F.Cu (tongue to U1's tab)"+    },+    {+      "net": "+VBAT",+      "polygon": [+        [+          127.58,+          59.8+        ],+        [+          159.13,+          59.8+        ],+        [+          159.13,+          93.0+        ],+        [+          160.6,+          93.0+        ],+        [+          160.6,+          111.0+        ],+        [+          153.3,+          111.0+        ],+        [+          153.3,+          95.0+        ],+        [+          127.58,+          95.0+        ]+      ],+      "layers": [+        "B.Cu"+      ],+      "priority": 2,+      "connection": "solid",+      "name": "+VBAT rail block, high-side tab column and bypass B.Cu"+    },+    {+      "net": "/DRV_SHA",+      "around": [+        "Q1.1",+        "Q2.5",+        "MC5"+      ],+      "margin": 0.6,+      "layers": [+        "F.Cu"+      ],+      "priority": 3,+      "connection": "solid",+      "name": "/DRV_SHA phase F.Cu"+    },+    {+      "net": "/DRV_SHB",+      "layers": [+        "F.Cu"+      ],+      "priority": 3,+      "connection": "solid",+      "name": "/DRV_SHB phase F.Cu (with the free band right of Q3)",+      "polygon": [+        [+          150.67,+          92.57+        ],+        [+          159.5,+          92.57+        ],+        [+          159.5,+          87.0+        ],+        [+          166.8,+          87.0+        ],+        [+          166.8,+          104.91+        ],+        [+          150.67,+          104.91+        ]+      ]+    },+    {+      "net": "/DRV_SHC",+      "around": [+        "Q5.1",+        "Q6.5",+        "MC11"+      ],+      "margin": 0.6,+      "layers": [+        "F.Cu"+      ],+      "priority": 3,+      "connection": "solid",+      "name": "/DRV_SHC phase F.Cu"+    },+    {+      "net": "GND_OUT",+      "around": [+        "MC3",+        "R32.1"+      ],+      "margin": 0.5,+      "layers": [+        "F.Cu",+        "B.Cu"+      ],+      "priority": 3,+      "connection": "solid",+      "name": "GND_OUT return"+    },+    {+      "net": "/DRV_SHA",+      "polygon": [+        [+          126.0,+          111.3+        ],+        [+          166.8,+          111.3+        ],+        [+          166.8,+          131.0+        ],+        [+          126.0,+          131.0+        ]+      ],+      "layers": [+        "B.Cu"+      ],+      "priority": 3,+      "connection": "solid",+      "name": "/DRV_SHA heat spreader B.Cu (under Q2 and the board's bottom band)"+    },+    {+      "net": "/DRV_SHB",+      "polygon": [+        [+          126.0,+          95.2+        ],+        [+          152.9,+          95.2+        ],+        [+          152.9,+          99.3+        ],+        [+          159.0,+          99.3+        ],+        [+          159.0,+          104.7+        ],+        [+          152.9,+          104.7+        ],+        [+          152.9,+          106.0+        ],+        [+          126.0,+          106.0+        ]+      ],+      "layers": [+        "B.Cu"+      ],+      "priority": 3,+      "connection": "solid",+      "name": "/DRV_SHB heat spreader B.Cu (left arm and the Q4 tab bridge)"+    },+    {+      "net": "/DRV_SHB",+      "polygon": [+        [+          161.0,+          87.0+        ],+        [+          166.8,+          87.0+        ],+        [+          166.8,+          108.6+        ],+        [+          161.0,+          108.6+        ]+      ],+      "layers": [+        "B.Cu"+      ],+      "priority": 3,+      "connection": "solid",+      "name": "/DRV_SHB heat spreader B.Cu (right arm, MC9 to above Q1)"+    },+    {+      "net": "/DRV_SHC",+      "polygon": [+        [+          131.0,+          79.0+        ],+        [+          159.0,+          79.0+        ],+        [+          159.0,+          86.5+        ],+        [+          152.9,+          86.5+        ],+        [+          152.9,+          93.0+        ],+        [+          131.0,+          93.0+        ]+      ],+      "layers": [+        "B.Cu"+      ],+      "priority": 3,+      "connection": "solid",+      "name": "/DRV_SHC heat spreader B.Cu (left arm and the Q6 tab)"+    },+    {+      "net": "/DRV_SHC",+      "polygon": [+        [+          159.6,+          73.57+        ],+        [+          166.8,+          73.57+        ],+        [+          166.8,+          86.41+        ],+        [+          159.6,+          86.41+        ]+      ],+      "layers": [+        "B.Cu"+      ],+      "priority": 3,+      "connection": "solid",+      "name": "/DRV_SHC heat spreader B.Cu (right arm at MC11)"+    }+  ],+  "solidAt": [+    "C45.2",+    "C43.2",+    "JP1.1"+  ],+  "thermalVias": [+    {+      "pad": "Q1.5",+      "net": "+VBAT",+      "count": 2+    },+    {+      "pad": "Q3.5",+      "net": "+VBAT",+      "count": 2+    },+    {+      "pad": "Q5.5",+      "net": "+VBAT",+      "count": 2+    },+    {+      "pad": "Q2.5",+      "net": "/DRV_SHA",+      "count": 2+    },+    {+      "pad": "Q4.5",+      "net": "/DRV_SHB",+      "count": 2+    },+    {+      "pad": "Q6.5",+      "net": "/DRV_SHC",+      "count": 2+    }+  ],+  "stitchVias": [+    {+      "net": "+VBAT",+      "x": 157.5,+      "y": 67.5+    },+    {+      "net": "+VBAT",+      "x": 145.5,+      "y": 61.0+    }+  ],+  "notPoured": [+    "/DRV_BSTA",+    "/DRV_BSTB",+    "/DRV_BSTC",+    "/SW",+    "/HSE_IN",+    "/HSE_OUT",+    "/COMP1_INM_IO1_BEMF_B",+    "/COMP1_INM_IO2_BEMF_C",+    "/COMP2_INM_IO1_BEMF_A",+    "/VBAT_SENSE",+    "/IBAT_SENSE",+    "+3V3",+    "/VDDA"+  ],+  "notPouredNote": "switching and bootstrap nodes, the crystal, the BEMF dividers and comparator inputs, the sense taps; +3V3 and /VDDA have the In2.Cu plane",+  "stitchViasNote": "inside both +VBAT rail blocks (F.Cu and B.Cu) and clear of other copper; a stitching via outside the pour is an unconnected item",+  "poursNote": "Phase current flows on F.Cu (source pads, low-side tab and connector are all F.Cu); the B.Cu phase zones are heat spreaders under the low-side tabs, fed by thermal vias. The +VBAT B.Cu block keeps a column under the high-side tabs and a bypass strip right of the FET column so Q1's tab copper stays connected past the Q4 tab bridge.",+  "hotNote": "BSC016N06NS at 20 A phase current: I2R at 3 mOhm hot (Rds(on) 1.6 mOhm typ at 25 C) over one third of the electrical cycle (six-step) is 0.4 W, plus about 0.2 W of switching at 24 kHz: 0.6 W per FET. U1 is a linear regulator on the +VBAT tab: 0.5 W.",+  "hotMaxRiseC": 60,+  "hotMaxRiseNote": "the tab may sit 60 C above ambient at the stated dissipation (theta from the 40 C/W per square inch outer-copper heuristic of 0.1)"+}
docs/video.mdadded+17
@@ -0,0 +1,17 @@+# The video, step by step (John, 2026-09-14)++The evidence of a run is its video, and the video is built from the steps, not recorded at the end.++1. **One clip per step.** `step <name>` stops the previous clip and starts this step's own window recording of the editor. The flow file's `record` line says what the clip should show.+2. **The step's code frames the shot.** A landing zooms to fit before it starts; a move lands with the part on screen; a refusal stays in the clip because the refusal is the story.+3. **The binary cuts the sped-up clip itself.** When a clip stops it is pulled into the run and a 10x version is cut next to it (a 51 s routing landing becomes a 5.1 s clip). Both go into the ledger as artifacts of that step, with their seconds: `{"event":"artifact","step":"routing","kind":"clip","seconds":51.0,"file":...}` and `kind:"clip10x"`.+4. **Every step records what it made.** The routed board, the pours plan, the zone state, the analysis tables, the per-step table: all artifacts in the ledger, so the final cut knows what exists without anyone remembering.+5. **The final video is composed, not edited.** At `deliver` the binary (0.2) reads the artifacts, picks one segment per step (the 10x clips, the analysis cards, the finish line), targets about two minutes, and writes the composition: segment order, each segment's seconds, and the words for that segment.+6. **The voiceover fits the picture.** For each segment the composer writes the narration, asks adom-tts for the audio, measures the audio against the segment's seconds, and if they do not match it re-composes the words (shorter or longer) and asks again, iterating until the audio fits the clip. Then gang-takes assembles the segments, the audio and the comparison charts.+7. **Two engines, one cut.** Because both runs carry the same steps, the same clip tags and the same artifacts, the split-screen comparison is the same composition run twice, side by side, with the per-step numbers from both ledgers on the charts.++0.1 does steps 1, 3 and 4. Steps 2, 5, 6 and 7 are 0.2: the composer, the adom-tts loop, the gang-takes split mode.++## Disk++Window capture writes H.264 from the box's GPU encoder as it records (no raw frames on disk, no screenshot polling) and only when the window changes, so a static board while the AI thinks costs almost nothing: the ESC replay was 128 MB for 17 minutes with routing landing the whole time, about 8 MB per minute of change. A monitor capture at 4K is about 115 MB per minute and is not used. The budget per step is the flow's expected minutes at 10 MB per minute, doubled for the pull and the 10x cut; `capture open` reports the whole run's budget against the run drive and the test box, and a step refuses to start its clip when either has less than the budget plus a 1 GB floor. Raw clips are kept as evidence next to their 10x cuts; the raw ones are the space, delete them from old runs first.
flows/board.jsonadded+119
@@ -0,0 +1,119 @@+{+  "name": "board",+  "title": "The board flow: parts placement to a qualified board",+  "scope": "Starts at parts placement on a board whose schematic, libraries and part choices already exist. Ends when the board is 100 percent routed, DRC-clean, poured, its copper measured, its current and thermal analyses passed, and the AI has delivered the video.",+  "steps": [+    {+      "name": "intake",+      "who": "ai",+      "does": "read the board and the spec, write the spec from the schematic if it is missing, plan",+      "record": "nothing on screen yet: the clip is the board opening on the test box (capture open) and the spec being read"+    },+    {+      "name": "placement",+      "who": "ai",+      "does": "place the parts for routability, current and heat; the binary packs, checks courtyards and lands moves",+      "binary": [+        "place pack",+        "place check",+        "land moves"+      ],+      "record": "the board on the test box with the parts landing as undo steps (land moves): the viewer sees the placement appear, one batch per decision"+    },+    {+      "name": "routing",+      "who": "binary",+      "does": "route to 100 percent with the spec's rules, gate with KiCad's DRC, land as undo steps; the AI takes it with take route=ai",+      "binary": [+        "route",+        "gate",+        "land route",+        "land vias"+      ],+      "record": "the routing landing net by net (land route, land vias): the longest clip, the one viewers speed up"+    },+    {+      "name": "pours",+      "who": "binary",+      "does": "pour every net the spec names, keepouts along Kelvin taps, thermal and stitching vias, land, measure the copper kept",+      "binary": [+        "pour",+        "land pours",+        "measure"+      ],+      "record": "the pours filling in (land pours) and the copper readback; keep the DRC refusals in the clip, they are the story"+    },+    {+      "name": "current",+      "who": "binary",+      "does": "IPC-2221 on every loaded net, pour and via capacity, the same-layer rule",+      "binary": [+        "analyze current"+      ],+      "record": "no screen action: the clip is the analysis table on the board (the page shows the numbers); keep it short"+    },+    {+      "name": "thermal",+      "who": "binary",+      "does": "copper and vias at every hot tab against the rise budget",+      "binary": [+        "analyze thermal"+      ],+      "record": "same: the hot tabs and their copper; when it fails, the return to pours or placement is the clip worth keeping"+    },+    {+      "name": "capture",+      "who": "binary",+      "does": "window recording with markers, so every engine's video lines up",+      "binary": [+        "capture open",+        "capture start",+        "capture mark",+        "capture stop"+      ],+      "record": "this step is the recorder itself: every step's clip is a window capture of the PCB editor, started when the step is declared"+    },+    {+      "name": "finish",+      "who": "both",+      "does": "finish refuses until everything is true; the AI cuts the video and delivers",+      "binary": [+        "finish",+        "deliver"+      ],+      "record": "the finish line passing (0 unconnected, 0 new errors), then the cut video and the delivery message"+    }+  ],+  "returns": "any step may send the AI back to any earlier step: step <name> --back --why \"...\"",+  "later": [+    {+      "name": "components",+      "does": "choose the parts from the requirements; SPICE may send the AI back here"+    },+    {+      "name": "libraries",+      "does": "symbols, footprints, 3D chips for every part"+    },+    {+      "name": "schematic",+      "does": "the schematic, and the spec that falls out of it"+    },+    {+      "name": "simulation",+      "does": "SPICE on the critical loops before the board exists"+    },+    {+      "name": "moleculize",+      "does": "machine pins so the board lives in the probing workcell"+    },+    {+      "name": "paste",+      "does": "solder paste jetting calculations"+    },+    {+      "name": "probe",+      "does": "how to probe the board when it comes off the InstaPCB process"+    }+  ],+  "clips": "every `step <name>` stops the previous step's clip and starts a new window recording tagged with the step, when the board is open on a test box; run.json captures[] carries one entry per clip with its step, start, stop and file, and deliver lists them; the final video is cut from these clips, one segment per step, so two engines' videos line up step for step; a return (step <name> --back --why) is a new visit and gets its own clip, tagged <step>-<visit> with the reason, so the rework is on camera and the final cut can show the loop"+}
install.shadded+12
@@ -0,0 +1,12 @@+#!/bin/bash+# adompkg install hook for adom-aiflow: the prebuilt binary to ~/.local/bin and the skill to ~/.claude/skills.+set -e+HERE="$(cd "$(dirname "$0")" && pwd)"+mkdir -p "$HOME/.local/bin" "$HOME/.claude/skills/adom-aiflow" "$HOME/.claude/skills/aiflow-measurement"+cp "$HERE/bin/adom-aiflow" "$HOME/.local/bin/adom-aiflow"+chmod +x "$HOME/.local/bin/adom-aiflow"+cp "$HERE/skills/adom-aiflow/SKILL.md" "$HOME/.claude/skills/adom-aiflow/SKILL.md"+cp "$HERE/skills/aiflow-measurement/SKILL.md" "$HOME/.claude/skills/aiflow-measurement/SKILL.md"+command -v service-kicad >/dev/null 2>&1 || echo "Hint: the offline gate needs service-kicad (adom-wiki pkg install adom/service-kicad)"+command -v adom-bridge >/dev/null 2>&1 || echo "Hint: the live stages need adom-bridge (the Adom Bridge CLI) and the KiCad Bridge on a desktop"+echo "OK: adom-aiflow installed at ~/.local/bin/adom-aiflow. Run 'adom-aiflow --version'."
package.jsonadded+83
@@ -0,0 +1,83 @@+{+  "slug": "adom-aiflow",+  "type": "app",+  "version": "0.1.0",+  "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.",+  "brief": "Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board.",+  "category": "Developer Tools",+  "tags": [+    "kicad",+    "pcb",+    "eda",+    "routing",+    "placement",+    "copper pours",+    "ablation",+    "current density",+    "thermal",+    "kelvin",+    "comparison"+  ],+  "status": "live",+  "discovery_pitch": "Take a KiCad board from parked parts to a routed, poured, analysed board through one command that keeps the clock, records who did each stage, and refuses to finish early. Both Claude and Codex run the same flow, so their numbers compare.",+  "discovery_triggers": [+    "100% routed",+    "ablation copper",+    "adom-aiflow",+    "ai flow",+    "aiflow",+    "board flow",+    "board loop",+    "compare engines on a board",+    "copper pours",+    "current density",+    "fable vs codex board",+    "finish the board",+    "flow for making a board",+    "from placement to a probed board",+    "kelvin keepout",+    "measure the ai per step",+    "place the parts",+    "route the board",+    "run.jsonl",+    "thermal analysis"+  ],+  "dependencies": {},+  "scripts": {+    "install": "./install.sh",+    "uninstall": "./uninstall.sh"+  },+  "visibility": "public",+  "install": {+    "binary_name": "adom-aiflow"+  },+  "hero": {+    "type": "image",+    "path": "screenshots/hero.png"+  },+  "bundled": true,+  "sample_prompts": [+    {+      "label": "Run the flow on a board",+      "prompt": "aiflow the ESC: place, route, pour, analyze and deliver the video"+    },+    {+      "label": "Route to 100 percent",+      "prompt": "route this board to 100 percent and refuse to finish with anything unconnected"+    },+    {+      "label": "Pours and thermal",+      "prompt": "pour the copper, keep the Kelvin taps clean, and tell me if any FET tab is too hot"+    },+    {+      "label": "Measure the AI per step",+      "prompt": "how long did the AI think at each step of the board flow, and where did it go back"+    },+    {+      "label": "Compare engines",+      "prompt": "run the same board flow on Fable and Codex and show me the comparison"+    }+  ]+}
screenshots/esc-finish.pngadded
⋯ 1 unchanged line ⋯
screenshots/hero.pngadded
⋯ 1 unchanged line ⋯
skills/adom-aiflow/SKILL.mdadded+51
@@ -0,0 +1,51 @@+---+name: adom-aiflow+description: The board loop as one command for KiCad boards on the Adom KiCad Bridge: start a measured run from the human's prompt, plan the stages and say which ones you take yourself (placement, routing) and which the binary runs (router, pours, gate, landing, measurement, current and thermal analysis), then finish only when the board is 100 percent routed, DRC-clean, poured, analysed and measured. One clock from the prompt to the finish line and one manifest per run, so Claude, Codex and any other engine compare on the same flow. Every command answers OK or ERROR with Hint lines that depend on this board and this run. Trigger words: aiflow, adom-aiflow, place and route the board, route the board to 100%, copper pours, current density, thermal analysis, finish the board, compare engines on a board, board loop, from placement to fab, kelvin keepouts, ablation copper, measured run, prompt-to-board time.+---++# adom-aiflow: the loop, measured++This tool is the place-route-pour-analyze skill made executable: the step order is a file, the gates refuse, the hints arrive as the answer to the command you just ran, and the ledger records what you did. Follow the commands and the skill follows itself.++Install: `adom-wiki pkg install adom/adom-aiflow` (one Rust binary; `service-kicad` for the offline DRC gate, and the KiCad Bridge on a desktop for the live stages). `adom-aiflow --version`.++Every state-changing command takes `--ai-thread "<your thread name>"`. Every run lives in one directory (`--run DIR`, default `./aiflow-run`) with a `run.json` manifest: the clock, the plan, the decisions, every stage's start and end, the outcomes, the capture markers and the summary. The manifest is the comparison; nothing counts that is not in it.++## The flow++The flow is a file, `flows/board.json`: the steps in order (intake, placement, routing, pours, current, thermal, capture, finish), who does each, what the binary offers at each, and the steps that come later (components, libraries, schematic, simulation before the board; moleculize, paste, probe after it). For now the flow starts at parts placement and ends at a delivered, analysed board. `plan` prints it.++1. **`start --board B.kicad_pcb --spec spec.json --engine <you> --prompt-time <UTC of the paste> [--target <box> --remote-board <path on the box>]`**: copies the board into the run and starts the clock at the prompt, not at your first command. Record the paste time honestly.+2. **`plan`**: the stages this board needs and who can take each. Placement is yours (the binary helps: `place pack --wish`, `place check --moves`, `place land --moves`). Routing is the binary's grid router by default; say `take route=ai` if you route yourself. Pours, gate, landing, measurement, both analyses and the finish are the binary's.+3. **`stage start <name>` / `stage end <name>`** around any stage you do yourself, so the clock and the manifest see it.+4. **`route [--passes N]`**: the grid router on the current board with the spec's rules (wide nets, planes, per-net vias, Kelvin taps). If it closes short, the ERROR names the pins and the blockers and the three levers: more passes, back to placement (`place pack` the parts it names or the test points and indicators in the escape band, `place check`, route again), or take the stage yourself.+5. **`gate`**: KiCad's DRC offline on the plan, inherited errors separated. Nothing lands until it passes.+6. **`land route`**, then **`pour`**, **`land vias`**, **`land pours`**: live through the bridge as native undo steps, each DRC-checked before it lands. A refused pour's ERROR says why (starved thermal reliefs, intersecting zones, clearance) and what to change in the spec. Both landings converge the live board on the plan, so you change the spec, run `pour` and land again: a via already at its planned place is not landed twice, a zone with its planned name and outline is kept, and every zone the flow landed that the plan no longer describes (renamed, reshaped, dropped) is removed first. Zones the board started with are never touched.+7. **`measure`**: KiCad's filled copper per layer, the ablation metric. **`analyze current`** and **`analyze thermal`**: IPC-2221 on the narrowest conductor of every loaded net, pour and via capacity, copper and vias at every hot tab. A loaded net whose pads all sit on one poured outer layer is carried by that layer's pour; its vias only spread heat. Hot parts take a theta ceiling (`maxThetaCPerW`) or a rise budget (`hotMaxRiseC` spec-wide, `maxRiseC` per part) against the stated watts, so write the dissipation physically (I2R at the hot Rds(on) over the conduction duty, plus switching) and say so in the spec. A FAIL names the net or part and the fix; fix it (pours, vias, widths, or placement), land, measure and analyze again.+8. **`finish`**, then **`deliver --video <mp4> --message "..."`**: `finish` refuses until the gate passed, routing and pours landed, copper measured, both analyses passed, and the live board validates at 0 unconnected with no new errors. Then it stamps the end and writes the summary: minutes from the prompt, minutes per stage, decisions, copper, live DRC. Add your token and dollar accounting to the manifest under `tokens` and `usd`.++## Capture, so the engines' videos line up++One clip per step. `capture open` puts the board on the test box; from then on every `step <name>` stops the previous step's clip and starts this step's own window recording, tagged with the step, and its hint says what that clip should show (the flow file's `record` line: the parts landing for placement, the nets landing for routing, the pours filling for pours, the return to an earlier step when an analysis fails). `deliver` lists the clips. The final video is cut from them, one segment per step, so two engines' videos line up step for step, and the page can show a little clip beside every step's numbers.++`capture open` puts the board on screen the same way for every engine (foreground OpenGL on a test box we own, maximised, zoom to fit). `capture start` records the PCB editor window itself through Adom Bridge's window recorder (Windows Graphics Capture, background-capturable: whatever another thread or an updater puts in front of KiCad on a shared box does not reach the take, and nobody at the box is disturbed; the monitor is recorded only when no editor is open), every stage command writes a marker with its timestamp into the manifest, `capture mark <label>` adds yours, `capture stop` pulls the MP4 into the run. The split-screen comparison (gang-takes split mode) aligns the two engines on those markers.++## The spec++`docs/spec-example.json` on the page is the ESC G431's: copper thickness, clearances, the inherited error count, fixed refs (the molecule interface), planes, wide and mid nets, Kelvin pairs, loads per net (amps, max rise), hot parts (watts, tab net), the pours (outline, around parts with a margin and exclusions, or explicit polygons; priorities and connection styles), solid patches, thermal and stitching vias, and the nets that are deliberately not poured. Write the spec from the schematic before you start; it is the electrical judgement, and it is what makes two engines' runs comparable.++## The rules behind the stages++The judgement is in these skills, which this tool executes: kicad-place-route-loop (place for routability, the levers when routing cannot close, go back to placement), kicad-copper-pours (which nets get a pour and which never do, priorities, the Kelvin pair, the preflight lessons), and the tool-neutral eda-engineering skillpack (eda-end-to-end-layout, eda-kelvin-current-sense, eda-copper-ablation, eda-pour-planning-measurement, eda-thermal-bottlenecks, eda-multilayer-current-review).++## What is measured (read aiflow-measurement)++The number is the human's prompt to your "done, here is your video" (`deliver`), and the cost per step is your thinking, not the binary's seconds. Every command you run is a turn in the append-only run.jsonl with the gap before it as thinking time (`adom-aiflow ledger` reads it back); declare `step <name>` as you move, and `step <name> --back --why "..."` when a later step sends you back. `finish` is the qualified board; `deliver --video --message` ends the clock.++## Honesty rules++- The clock starts at the prompt. A late `start` takes `--prompt-time`.+- A stage you take yourself is stamped by you and named in the manifest as `ai`; the binary's stages are `binary`. Both go on the chart.+- `finish` is the only thing that ends a run. A run without `finish` is not a result, and its minutes are still counting.+- KiCad drops pour islands that touch nothing: a heat spreader drawn across dense routing on the other layer fills as fragments and reads small in `measure`. Put the copper where the layer is actually free (the analysis numbers say when it is not).+- 0.1's analyses are conservative heuristics (IPC-2221 for tracks, presence, connection style and vias for pours and tabs); they say so in their output. 0.2 computes cross-sections through the filled copper.
skills/aiflow-measurement/SKILL.mdadded+42
@@ -0,0 +1,42 @@+---+name: aiflow-measurement+description: How an adom-aiflow run is measured, in John's words (2026-09-14). The number is the wall clock from the human's first prompt to the AI saying "done, here is your video", and the per-step cost is the AI's thinking, not the binary's seconds. Every command is a turn in run.jsonl with the thinking gap before it in an append-only run.jsonl; every return to an earlier step (placement moved because a pour had bad heatsinking) is recorded with its reason. Read this before you start, run, report, or compare a aiflow run, and before you hand a run to another engine. Trigger words: aiflow stats, measure the run, how long did each step take, prompt to done, AI thinking time, iterative rework, go back to placement, fable vs codex comparison, deliver the video, run.json ledger.+---++# Measuring a aiflow run++What is being measured is the AI. Deterministic code is fast; the AI thinking its way through the board, the placement, the routing, the pours, the current and thermal work is slow, and that is the cost the chart has to show. Reporting the binary's stage seconds ("route 25 s", "capture open 51 s") while the human sat watching the AI think for twenty minutes is wrong: it took twenty minutes.++## The clock++- Starts at the human's first prompt that starts the aiflow. Pass it exactly: `start --prompt-time <UTC of that message>`.+- Stops when the AI says "I'm 100 % done. Here is your video. Check it out." That moment is `deliver --video <mp4> --message "<what you said>"`. `finish` (the qualified board) is a milestone inside the run, not the end of it.+- Nothing inside is excluded. Waiting for the human, an overnight gap, a re-take: all of it counts. If a gap should be explained, explain it in a marker; do not subtract it.++## The ledger (run.jsonl, append-only)++- `run.jsonl` is the record: one JSON line per event, appended, never rewritten (a crash or a rewrite cannot lose a turn). Events: `start`, `turn` (a command begins), `turn-end`, `step` (a visit, with `back` and `why`), `stage-start`/`stage-end` (the binary's own work), `outcome`, `marker`, `clip-start`/`clip-stop` (one clip per step), `artifact` (what a step made: the clip and its seconds, the 10x cut, the zone state, the plans), `finish`, `deliver`. The final video is composed from the artifacts (docs/video.md). `run.json` is the derived state and summary; when they disagree, the ledger wins. `adom-aiflow ledger` prints it in order with the per-step table; `--json` streams the lines.+- `turn`: every command the AI runs is a turn: the step it belongs to, the command, when it began, `thinkingSeconds` (the gap since the previous turn ended, or since the prompt for the first one: the AI's thinking that led to this command), `binarySeconds` (the tool's own time), and the first line it answered. The binary writes this itself; nothing to remember.+- `steps`: the AI declares what it is working on with `step <name>` (intake, placement, routing, pours, current, thermal, capture, finish, or its own name). Every command until the next `step` is charged to it. A return to an earlier step is `step <name> --back --why "<what sent you back>"`; the reason is part of the record.+- `summary.steps` at `finish` and `deliver`: per step, wall = thinking + tool, turns, visits, returns, longest single think. The sum over steps is the whole run. `promptToDeliveredMinutes` is the headline number; `promptToFinishMinutes` is the milestone.++## Iteration is expected++The AI can do a fine placement early and only find out at the pours that a low-side tab has no room for copper, or at the thermal step that a heat spreader fills as islands under dense routing. Then it goes back: moves parts, moves vias, reroutes, pours again, analyzes again. That is the same iterative analysis a human does, and every return is a visit with a reason in the ledger. Do not hide rework by editing the spec quietly: declare the step, say why, do the work, come forward again.++## Evidence the human will check++The final video and the page are the evidence that the run is any good. They have to show, per step, the real length of time (the AI's), and the substance: the copper pour current analysis, the heat analysis, the copper kept for ablation, and what was refused and why. A video that shows a 51-second stage the human waited twenty minutes for is not evidence.++## Before a comparison++Get it right with one engine first, on a fresh prompt, from `start` to `deliver`, and read the ledger back: does every step carry its thinking time, do the returns say why, does the video match the ledger. Only then hand the same board, spec, flow and prompt to the other engine, so both runs measure the same thing.++## Honesty++- A run that was assembled from earlier work with a back-dated prompt is a proving run of the tool, not a measured run of the AI. Say so.+- If you have not run the flow end to end from a prompt, the answer to "how long did it take you" is "I have not run it yet; I have to attempt it for the first time to answer."++## Cost, two ways++Each engine writes its token counts and the dollar figure at API rates into run.json (`tokens.in`, `tokens.out`, `usd.api`). The chart shows that figure, which is what commercial users on API plans pay, and beside it the same figure divided by 20 (`usd.plan20x`), which is the real cost on a 20x subscription plan. Both lines, always; neither alone tells the story.
uninstall.shadded+4
@@ -0,0 +1,4 @@+#!/bin/bash+rm -f "$HOME/.local/bin/adom-aiflow"+rm -rf "$HOME/.claude/skills/adom-aiflow" "$HOME/.claude/skills/aiflow-measurement"+echo "OK: adom-aiflow removed."