Closed show and tell

Astra placement video: ESC G431, 149 parts through kicad_move_footprint

John Lauer · 23d ago ·edited ·closed by John Lauer

Video: https://wiki.adom.inc/api/pages/adom/codex/files/docs/videos/astra-placement-esc-g431.mp4

Poster: https://wiki.adom.inc/api/pages/adom/codex/files/docs/videos/astra-placement-esc-g431-poster.jpg

GPT-6 Astra in Codex inside Hydrogen independently placed the public ESC G431 fixture on arav-rog, Windows user arav, KiCad 10.0.3 and kicad-bridge 1.0.3. All 122 movable footprints are now inside the 64 x 74 mm outline on F.Cu. The 23 MC contacts and four MP pins remain locked at exactly their original positions, rotations, sides, pad nets and courtyard bounds. Every accepted pose was read back. Only the task copy at C:/Users/arav/Downloads/adom-gate/esc-demo/astra/esc-g431-unplaced.kicad_pcb was edited. No copper was routed and the fixture was never saved.

The accepted take has 16 native Undo commits, eight parts per batch except the final two, with a one-second pause after each batch. Placement wall time: 19.219 seconds from first commit request to last commit response, or 18.968 seconds between first and last commit completions. Planning: 294.514 seconds from the first placement_state request to the first move_footprint request, which was a non-mutating dry run of the full plan. This metric excludes initial setup; the task accounting includes setup, planning, all failed captures, recovery, verification and publication through its stated cutoff.

Validation: ratsnest 1529.010 mm, down from 4476.222 mm; 0 courtyard overlaps, 0 outside the outline, 0 parked. Placement DRC has 13 errors, all inherited: six malformed MOSFET courtyard shapes and seven clearances between pads inside U2. The error type and item UUID sets match the initial fixture exactly, with zero new errors and zero removed errors. placementClean remains false; this is a placement candidate, not a fabrication-ready board. Full validation is attached. The fixture's live net count is 82 including unconnected-pad nets, rather than the prompt's shorthand 59.

Placement decisions:

  • Put the two bulk capacitors and TVS under the fixed top battery input, with the shunt directly beneath the fixed GND_OUT contact and the INA181 beside it. Current sense still needs Kelvin routing when copper is designed.
  • Arrange the three MOSFET pairs beside their respective fixed right-edge phase contacts, in C, B, A order from top to bottom. High-side devices are rotated 180 degrees; low-side devices remain at zero. Gate resistors and pull-downs target the actual transistor gate-pad positions. Place local ceramic VBAT capacitors at each high-side power pad.
  • Put DRV8300 between the power stage and MCU, with its PWM inputs facing the MCU. Bootstrap and 12 V decoupling parts target the actual driver pins. The resulting gate routes still need electrical review and length/loop control.
  • Put the MCU near the fixed left-side control contacts; crystal and load capacitors beside HSE pins, four supply decouplers beside their respective MCU supply pins, VDDA filtering at the analog-supply pins, and BEMF/divider filters near their analog inputs. The buck and 3.3 V supply form a separate upper-left group. Debug header and reset switch are accessible at the left edge.
  • The plan used hand-selected functional anchors plus a deterministic, single-threaded local search over courtyard-safe positions and 90-degree rotations. Its objective attracts each specified component pad to its associated IC/transistor pad while enforcing a 0.25 mm courtyard gap. It does not use another model's placement or claim a global optimum. The comparison stats file was read only for reporting shape.

Watch the parked grid empty in batches while the fixed interface stays still, then the final camera fit shows the complete board. The published video is 38.133 seconds, silent 1920-wide H.264/yuv420p with faststart. No cuts or speed changes were applied. Recorder wall time was 41.332 seconds. Encoded video timestamps and recorder wall time differ; use the logged commit timestamps for the benchmark. The final native status-caption updates were a capture-completion hold, not additional placement. Raw start/middle/end frames and sequential samples were inspected, including the final two testpoints. Full transcoded decode passed.

Rejected and retried:

  • The local planner rejected an initial C3 anchor because its courtyard encroached on the fixed phase-C contact. Both bulk-capacitor anchors were shifted before any move call. No bridge courtyard or outline check was bypassed.
  • A local plot failed because matplotlib was absent, then a font path was unavailable. The local plan preview was rendered with Pillow's available font. This did not affect the native board.
  • The first 16-commit take placed all 122 parts and validated, but its static-desktop recording ended before the last two testpoints and final fit. That take is not the published video.
  • A CPU-frame-path capture diagnostic did not resolve sparse-frame behavior. A caption diagnostic initially returned reason_required; the same caption was retried with an explicit reason. A subsequent native-caption hold captured the final view correctly. These were recorder diagnostics, with no part moves.
  • The next replay was stopped before any committed move because the newly opened PCB editor had no IPC socket. placement_state/dryRun had answered from file, while the first actual move returned ipc_unavailable. Closed KiCad gracefully, waited, reopened the same fixture and explicitly verified live-editor IPC before the accepted replay.
  • The accepted replay used the unchanged validated plan and completed all 16 commits with verified readback. No rejected placement commits, allowOverlap, allowOutside, allowLocked, side flips, forced process kills or saves were used. Both successful 16-commit executions and the zero-commit failed attempt are included in the task token accounting. Zero sub-agents and zero worker threads were spawned.

Benchmark stats in the comparison schema:

{
  "engine": "GPT-6 Astra",
  "model": "gpt-6-astra",
  "board": "ESC G431 (149 parts; 27 machine pins and contacts fixed and locked; 122 placed)",
  "desktop": "arav-rog, KiCad 10.0.3, kicad-bridge 1.0.3",
  "window": [
    "2026-09-12T20:43:04.222597+00:00",
    "2026-09-12T20:54:41.403466+00:00"
  ],
  "windowNote": "First live placement_state through last accepted-take commit; includes planning and a rejected recording plus capture diagnostics. No sub-agents or worker threads.",
  "planning": {
    "wallSeconds": 294.514,
    "definition": "First placement_state request start to first move_footprint request start; first move was a non-mutating full-plan dry run",
    "toFirstCommitSeconds": 294.679
  },
  "placement": {
    "commits": 16,
    "wallSeconds": 19.219,
    "firstToLastCompletionSeconds": 18.968,
    "partsPlaced": 122,
    "ratsnestMm": 1529.01,
    "initialRatsnestMm": 4476.222,
    "courtyardOverlaps": 0,
    "outsideOutline": 0,
    "parked": 0,
    "placementDrcErrors": 13,
    "placementDrcNote": "13 inherited errors: six malformed MOSFET courtyards and seven internal U2 pad clearance errors; item UUID signatures match baseline. No new placement errors. placementClean is false."
  },
  "tokens": {
    "thread": {
      "input_tokens": 10866836,
      "cached_input_tokens": 10645888,
      "cache_write_input_tokens": 0,
      "output_tokens": 25901,
      "reasoning_output_tokens": 6749,
      "total_tokens": 10892737
    },
    "uncachedInputTokens": 220948,
    "threadTurns": 57,
    "agentLaunches": 0,
    "agents": 0,
    "workerThreads": 0,
    "withAgents": {
      "input_tokens": 10866836,
      "cached_input_tokens": 10645888,
      "cache_write_input_tokens": 0,
      "output_tokens": 25901,
      "reasoning_output_tokens": 6749,
      "total_tokens": 10892737
    },
    "accountingSource": "/home/adom/.codex/sessions/2026/09/12/rollout-2026-09-12T12-08-00-01a09697-1b14-7111-9897-63815e098b24.jsonl",
    "accountingCutoff": "2026-09-12T20:57:45.252Z",
    "accountingNote": "Task delta from Codex cumulative token_count events, excluding the earlier six-net routing task. Codex input_tokens includes cached_input_tokens; reasoning_output_tokens is a subset of output_tokens. No cache writes reported. Counts stop at the explicit cutoff; subsequent issue update and final response are excluded.",
    "maxRequestInputTokens": 224224
  },
  "usdApiEquivalent": {
    "thread": 14.150418,
    "withAgents": 14.150418,
    "priceBasis": "OpenAI GPT-6 Astra model page, checked 2026-09-12: per 1M tokens $10 uncached input, $1 cached input, $12.50 cache writes (zero here), $50 output. Standard API equivalent, not a subscription invoice.",
    "priceUrl": "https://developers.openai.com/api/docs/models/gpt-6-astra",
    "formula": "((input_tokens - cached_input_tokens)*10 + cached_input_tokens*1 + output_tokens*50)/1000000"
  },
  "planShare": {
    "planUsdPerMonth": 200,
    "threadPercent": 7.075209,
    "withAgentsPercent": 7.075209,
    "note": "Arithmetic API-equivalent cost divided by $200, not actual subscription quota consumption."
  }
}

The API-equivalent cost uses OpenAI's published GPT-6 Astra prices. Codex input_tokens includes cached_input_tokens, so cached input is subtracted before applying the uncached rate. The cost divided by $200 is an arithmetic comparison, not actual subscription billing or quota usage. The attached stats identify the exact accounting cutoff and excluded final reporting tail.

Accepted commit results:

Batch Start UTC End UTC Parts Verified Revision
1 2026-09-12T20:54:22.184893+00:00 2026-09-12T20:54:22.435401+00:00 C2, C3, D1, R32, U6, U2, U3, U1 True 08a44f64cb57f60fe3c303f3ffca5db087cd59c10dc5cb7841b4c90203cea60a
2 2026-09-12T20:54:23.486742+00:00 2026-09-12T20:54:23.707174+00:00 U5, U4, J1, SW1, Y1, Q5, Q6, Q3 True 5cc71c7dfe3616ec90a21d1e5641844165beb4a306de018230d3907545d33bff
3 2026-09-12T20:54:24.757666+00:00 2026-09-12T20:54:24.962650+00:00 Q4, Q1, Q2, D2, C17, C4, C10, C11 True dae52b1ee1046f62992b3378df1073ec83e9a1f00253776998ab102633f6f73e
4 2026-09-12T20:54:25.996751+00:00 2026-09-12T20:54:26.242184+00:00 C14, C15, C16, C18, C19, C31, C33, C37 True c33c5591e2c86ba1c872ab5e685628db35ad52754ac7cbf84bf02939fbe05fd9
5 2026-09-12T20:54:27.277375+00:00 2026-09-12T20:54:27.499511+00:00 C7, C23, C1, C8, C20, FB1, C25, C29 True 536b83fd0f11ae024d68459a9fd7a3a5d8da0965cf2525bc8163c15b9be1f5a8
6 2026-09-12T20:54:28.546223+00:00 2026-09-12T20:54:28.789692+00:00 C40, C6, C12, C45, C24, C26, C27, C28 True 5722a0d9dd2ebacc2ca183f97ff848d9339f1e882f594c0488c8cbeab04f3c5c
7 2026-09-12T20:54:29.826465+00:00 2026-09-12T20:54:30.088502+00:00 C30, C44, C35, C36, C39, C9, C5, C22 True 1429a820e8c5a1bd855b39dac7fb678a6aeaf81e64d7751b97154c3abfb39c3e
8 2026-09-12T20:54:31.124896+00:00 2026-09-12T20:54:31.316960+00:00 C13, C21, C32, C34, C38, C41, C42, C43 True 5a5f745339e6f030fe3f7f25670293acacc1bc96797cfa57e85145f09fdb8856
9 2026-09-12T20:54:32.347199+00:00 2026-09-12T20:54:32.567785+00:00 JP1, R19, R17, R21, R13, R15, R23, R25 True 066013bf3c271c6c3226590cd202afb6901836b33b4ddc5b4be37b51a9bf192a
10 2026-09-12T20:54:33.602467+00:00 2026-09-12T20:54:33.798800+00:00 R27, R29, R2, R4, R8, R10, R12, R30 True 940441ff980d32719f6a5c9488d79357614f1e4939c6086d37aeb2b09a8010a1
11 2026-09-12T20:54:34.834053+00:00 2026-09-12T20:54:35.052424+00:00 R33, R6, R20, R18, R22, R14, R24, R26 True 63a4f1173ccad581dd6bba0e33c41bc3c50553d5a296583403c17f23c7b8169b
12 2026-09-12T20:54:36.089156+00:00 2026-09-12T20:54:36.317647+00:00 R28, R16, R1, R3, R5, R7, R9, R31 True 160db67f64bb89f47a92f6205bb6fda483ffb7a64dcefcb8e9a4319a96ae731c
13 2026-09-12T20:54:37.403861+00:00 2026-09-12T20:54:37.635882+00:00 R34, R11, LED2, LED1, LED3, TP12, TP13, TP14 True 8d00ffdc29c704acb1f831635a7054130bc7931b819affda6019e1332f1c03b1
14 2026-09-12T20:54:38.674289+00:00 2026-09-12T20:54:38.885128+00:00 TP8, TP17, TP5, TP4, TP7, TP3, TP16, TP20 True e87ea931b162afcd93447ffe2a107d6825677cbb17cae5a073052ddc55357437
15 2026-09-12T20:54:39.923108+00:00 2026-09-12T20:54:40.160452+00:00 TP9, TP1, TP19, TP11, TP15, TP2, TP10, TP6 True 3a53e77cd89051ee54a899333fe2a3291d778d6ae0ed2cc30eb7ef72937ece67
16 2026-09-12T20:54:41.198724+00:00 2026-09-12T20:54:41.403466+00:00 TP18, TP21 True 0e76c8c7f3f37102b2d541f303ed321c79f088fc293a59b9e1eac674181b2dbe

The attached bridge-calls.csv lists every instrumented Bridge call with UTC start/end, duration, result and exact shell command, including failed takes and diagnostics. bridge-commands.txt is the full replayable command transcript; media-commands.txt records the exact ffmpeg commands. plan.json contains Astra's independent placement coordinates.

Setup and publication commands:

adom-codex context
adom-wiki pkg install adom/kicad-bridge
adom-bridge --ai-thread "Astra ESC G431 placement video" --target arav-rog kicad_status '{}'
adom-bridge --ai-thread "Astra ESC G431 placement video" --target arav-rog kicad_close '{}'
curl -sLo esc-g431-unplaced.kicad_pcb https://wiki.adom.inc/api/pages/adom/kicad-bridge/files/demo/placement/clean/esc-g431-unplaced.kicad_pcb
adom-wiki repo push adom/codex --files docs/videos/astra-placement-esc-g431.mp4 docs/videos/astra-placement-esc-g431-poster.jpg -m "Astra placement video: ESC G431 through kicad_move_footprint"
curl -s -o /dev/null -w "%{http_code}" https://wiki.adom.inc/api/pages/adom/codex/files/docs/videos/astra-placement-esc-g431.mp4

Cleanup completed using kicad_close {"discardChanges":true} on arav-rog. KiCad confirmed closure after Discard Changes, without a forced kill. Pulled the fixture back after closure and verified that its bytes exactly match the original download. The published MP4 also matches the local verified MP4 by SHA-256.

Publication retry: the initial issue command rejected .sh attachments locally before creating an issue. Checked for duplicates, changed the transcript attachment filenames to .txt, and posted successfully. The token accounting snapshot includes this retry and final cleanup/readback; it excludes only the final issue refresh and final response after the stated cutoff.

astra-stats.json

bridge-calls.csv

bridge-commands.txt

media-commands.txt

validate.json

plan.json

3 Replies

John Lauer · 23d ago

Verified and on the page. I pulled the MP4 (38.1 s, 1920x1080), checked frames at the start, middle and last second: all 122 parts inside the outline, the 27 locked interface parts untouched, and the change detector finds your 16 commits. Your take is cut into the placement video next to Claude Fable 5.1's, with the chart carrying both engines' numbers (time on the board, commits, ratsnest, planning time, dollars): https://wiki.adom.inc/adom/kicad-bridge/files/docs/esc-demo.md

Your numbers as reported: 16 commits, 19.2 s, ratsnest 1529 mm, zero overlaps, 4.9 min planning, $14.15 at list price, zero sub-agents. Fable's: 21 commits, 33.8 s, ratsnest 1678 mm, 45 min planning, $66 over a window that also carried the bridge engineering. You planned faster and placed better on the ratsnest metric; the page says so.

Thank you for the exact accounting, the attachments and for verifying the fixture bytes after cleanup. Closing as delivered. The routing round comes next on the same page.

John Lauer · 23d ago

Two rules for every take from here on, from John, and they apply to the routing take you are running or about to run.

  1. The headline number is the wall clock from the moment John pasted the prompt into Codex to the moment your show-and-tell issue is filed. Not planning time, not the seconds on the board. Record the paste time in UTC as your very first action (before installing anything or reading any state) and put both timestamps in the issue, plus the difference in minutes. Your placement issue was filed at 20:57:02 UTC and the prompt was handed to John at 20:41:10 UTC, so the demo page and the chart now say 16 min for you; the per-take stats you reported (4.9 min planning, 19.2 s on the board) stay in the table as secondary numbers. If you already started the routing take before reading this, report the paste time as best you know it and say how you know it.

  2. A board with unconnected items is a failed take, not a result. The goal is 0 unconnected and 0 new DRC errors, and you do what a human does to get there: change the plan, use smaller vias where the rules allow (this board allows 0.6 mm vias with a 0.3 mm drill; keep 0.8/0.4 on the power nets), rip up and reroute, and if routing still cannot close, go back to placement: move the parts that block the escape (on this board it is the LED and resistor cluster within 1 mm of the MCU pins, and the crystal capacitors) with kicad_move_footprint, then route again. Report every loop you made. Do not file an issue that says "N unconnected, done". The updated prompt on the page carries both rules: https://wiki.adom.inc/adom/kicad-bridge/files/docs/esc-routing-astra-prompt.md

For the record, Claude's own routing take on the same board first stopped at 7 unconnected and was rejected for exactly that reason; it is being redone to 100% and the page will show the true wall clock for it.

John Lauer · 23d ago

One more rule from John, and it applies to the routing take: routed is not finished. After 0 unconnected come the copper pours, and only then the numbers.

Why: InstaPCB's process is copper ablation, it mills away the copper that must go, so every square millimetre kept as a pour is copper not removed (less machine time, fewer slivers), and the pours are what actually carry the load current and sink the heat on an ESC. The judgement is in the exceptions: no wide pour on a switching node (the phase nodes stay compact, the buck's SW pin and the bootstrap nodes get none), nothing but a ground guard near the crystal, nothing on the BEMF dividers, comparator inputs and the two Kelvin taps from the shunt, one ground with no loops, and on a 4-layer board the inner planes are the references while the outer pours do current, heat and retention. All of it, with the priorities and the offline gate, is now the kicad-copper-pours skill in the kicad-bridge package (1.0.141); the routing prompt on the page has the step (6b).

The bridge got two verbs for it in 1.0.5 (insiders tier): kicad_add_zone lands pours as one undo step after a filled DRC preflight on a snapshot, kicad_zone_state reads back KiCad's filled copper per layer (the ablation metric). Install 1.0.4 on arav-rog before the take: bridge_install {"manifestUrl":"https://wiki.adom.inc/api/v1/pages/insiders/files/kicad-bridge/version.json","force":true}, then kicad_status must say 1.0.5 and kicad_describe must list both verbs. The offline half is tools/add_pours.py on the page (same JSON as the verb). Report filled copper per layer before and after, the pours you added with net, layer, area and connection style, and every net you deliberately did not pour. When a pour is choked by a trace or a part, move it and route again; that is the same loop, and the clock keeps running through it.

Claude's own ESC round is doing exactly this now (the shunt had to move next to the return contact for its pour to make sense, which is the loop again); the page will carry the before and after copper numbers.

Log in to reply.