app
AI Flow
Public Made by Adomby adom
Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board.
main
John Lauer
Publish 0.2.48
7fce739
"""Issue #47: rules install honours --layers, never silently replaces another layer pack, and
rules check falls back to run.json fabRules and judges jlcpcb by the board's copper layers."""
import json,os,pathlib,subprocess,sys,tempfile
binary=str(pathlib.Path(sys.argv[1]).resolve())
SIX='(kicad_pcb (version 20260101) (layers (0 "F.Cu" signal) (1 "In1.Cu" signal) (2 "In2.Cu" signal) (3 "In3.Cu" signal) (4 "In4.Cu" signal) (31 "B.Cu" signal) (44 "Edge.Cuts" user)) (gr_rect (start 0 0) (end 10 10) (layer "Edge.Cuts")))\n'
PACK6='# adom-aiflow rules: jlcpcb 6L (from adom/pcb-design-rules)\n(version 1)\n(rule "x" (constraint clearance (min 0.1mm)))\n'
with tempfile.TemporaryDirectory(prefix='aiflow-rules-layers-') as td:
p=pathlib.Path(td);run=p/'run';env=dict(os.environ,AIFLOW_DASHBOARD='off')
def call(*args,inrun=True):
pre=['--run',str(run)] if inrun else ['--run',str(p/'none')]
return subprocess.run([binary,*pre,'--ai-thread','rules-layers',*args],env=env,capture_output=True,text=True,timeout=30)
board=p/'b6.kicad_pcb';board.write_text(SIX);dru=board.with_suffix('.kicad_dru')
# 1. install with --layers 6: no shipped pack, an error that points at `rules use`, nothing written
r=call('rules','install','--profile','jlcpcb','--layers','6','--board',str(board),'--force',inrun=False)
assert r.returncode!=0 and 'rules use --profile jlcpcb --layers 6' in (r.stdout+r.stderr),(r.stdout,r.stderr)
assert not dru.exists()
# without --layers the board's 6 copper layers decide: still not the 2L pack
r=call('rules','install','--profile','jlcpcb','--board',str(board),inrun=False);assert r.returncode!=0 and not dru.exists()
# --layers 2 on a 6-layer board is refused
r=call('rules','install','--profile','jlcpcb','--layers','2','--board',str(board),'--force',inrun=False);assert r.returncode!=0 and not dru.exists()
# 2. the 6L pack passes as jlcpcb and jlcpcb-6L; the 2L file fails with the right command
dru.write_text(PACK6)
for prof in ['jlcpcb','jlcpcb-6L']:
r=call('rules','check','--profile',prof,'--board',str(board),inrun=False);assert r.returncode==0,(prof,r.stdout,r.stderr)
# 3. install on a 2-layer board over a 6L pack: --force replaces it and says so, keeping a .bak
two=p/'b2.kicad_pcb';two.write_text('(kicad_pcb (version 20260101) (layers (0 "F.Cu" signal) (31 "B.Cu" signal)))\n');two.with_suffix('.kicad_dru').write_text(PACK6)
r=call('rules','install','--profile','jlcpcb','--board',str(two),inrun=False);assert r.returncode==0 and 'jlcpcb-6L' in (r.stdout+r.stderr) and 'replaced' in (r.stdout+r.stderr),(r.stdout,r.stderr)
r=call('rules','install','--profile','jlcpcb','--board',str(two),'--force',inrun=False);out=r.stdout+r.stderr
assert r.returncode==0 and 'jlcpcb-2L' in out,out
assert two.with_suffix('.kicad_dru.bak').read_text()==PACK6
r=call('rules','check','--profile','jlcpcb','--board',str(two),inrun=False);assert r.returncode==0,(r.stdout,r.stderr)
# 4. a run whose spec names no fab.rules falls back to run.json fabRules
assert call('intake','--prompt-time','2026-10-10T00:00:00Z','--engine','test').returncode==0
spec=p/'spec.json';spec.write_text(json.dumps({}))
state=run/'run.json';d=json.loads(state.read_text());d['boards']=[str(board)];d['spec']=str(spec)
d['fabRules']={'profile':'jlcpcb','slug':'jlcpcb','layers':6,'file':'jlcpcb-6L.kicad_dru'};state.write_text(json.dumps(d))
r=call('rules','check');assert r.returncode==0,(r.stdout,r.stderr)
o=json.loads(state.read_text())['outcomes']['rules'];assert o['pass'] and o['profile']=='jlcpcb',o
dru.write_text('(version 1)\n# adom-aiflow rules profile: jlcpcb-2L\n(rule "x")\n')
r=call('rules','check');assert r.returncode==0 and 'FINDINGS' in r.stdout and '--layers 6' in (r.stdout+r.stderr),(r.stdout,r.stderr)
# the spec disagreeing with fabRules warns
spec.write_text(json.dumps({'fab':{'rules':'jlcpcb-2L'}}))
r=call('rules','check');assert 'fabRules' in r.stderr,(r.stdout,r.stderr)
print('PASS: install honours --layers and the board, refuses a missing shipped pack, reports a replaced pack; check reads fabRules, the board layer count and warns on disagreement.')