Implementation plan: requirements, design as text, project scripts¶
A source
This page is a source of edakit – a concept, a prompt or the rules of a session –, shown as it is in the repository. Read it as such; the guides say what edakit does.
Status 2026-11-01: done – moved to done/ on the owner's word. Built: phases 1 to 3, phase 4 – 4.4 in pytree, its
D-50 to D-54; 4.5, the requirements file required, as DOCS.file – and phase 5; the live runs of 2.1 to 2.4, 3.6, 5.7,
5.8, 5.10 and 5.12 passed (2026-10-31 and 2026-11-01, results under each; tests/fixtures/makers/LIVE.md), nothing
ordered – no order has gone through edakit cart actual yet (TODO.md TD-57); 5.11 dropped – future work, nobody
asks the makers.
The plan built five concepts, decided by the user on 2026-10-28 (e5caf42, 301fd90) and 2026-10-30:
requirements.md, design-as-text.md,
project-scripts.md, project-setup.md and ordering.md; the
circuit's form has a plan of its own, circuit-language-plan.md, built up to L.9.
Ground rules¶
- One step, one branch. A step is built with the skill add-feature, a new check with add-rule: a failing test
first, then the interface, the code, the handbook,
pytestandruff. It goes to main when the user says so, after a notice to pytree-27; then the shared tree follows, and the plugin when skills or code changed. - No key, no change. Every new key, file and check leaves a project without it as it is. The bell and the reference project change only at the user's request, in their own repositories.
- Hashes are named. A step that changes a hash says which one, and for which sign-offs (table at the end).
- The bell is told. All sessions run the edakit of the shared tree. The user decided on 2026-10-28 not to wait for the bell's order: a step goes to main when it is ready, and one that changes the bell's gate result or hashes is announced to the bell's session first.
- EasyEDA steps in one sitting. Steps that need EasyEDA take its claim, probe on a throwaway document (working agreement 18) and run together while EasyEDA is free.
- This file is the list. The state of each step is in the overview; a finished step names its commit.
Overview¶
| Step | What | Concept, decision | Size | EasyEDA | Waits for | State |
|---|---|---|---|---|---|---|
| 1.1 | README: "circuit design as code", with "Status and limits" | design-as-text 1, 2 | S | no | – | done (ef6b2aa) |
| 1.2 | Read [scripts.*] and check that they resolve into a DAG |
project-scripts block 1, decision 1 | M | no | – | done (37376e8) |
| 1.3 | Names for edakit's data, each with a hash | project-scripts block 2 | S | no | 1.2 | done (74279e8) |
| 1.4 | Stamps in hardware/scripts.lock; edakit scripts |
project-scripts block 3, decision 2 | M | no | 1.3 | done (af6d0b9) |
| 1.5 | Requirements: format, parser, edakit req |
requirements order 1, decisions 1–3 | M | no | – | done (ba4d28b, f7adcf4) |
| 1.6 | Requirements: checks, protocol section, accept lines, circuit hash |
requirements order 2, decisions 4, 6, 8 | M | no | 1.5 | done (384385e) |
| 1.7 | [order]: mask and print colour, thickness from thickness_mm |
design-as-text, smaller findings | S | no | – | done (80c750e) |
| 1.8 | The footprint geometry in the repository | project-scripts block 5, decision 5 | S | no | – | done (d63a81a) |
| 2.1 | Positions of the auto-layout as text, one-time takeover, every part in the plan | design-as-text gap 1, decisions 3, 4 | M | yes | – | built offline (5d8a5df), live run passed 2026-10-31 |
| 2.2 | Rule set of EasyEDA's DRC | design-as-text decision 6 | S–M | yes | – | built offline (a71567f), live probe 2026-10-31: writing holds; mode "write" built the same day |
| 2.3 | Positions of single designators in [silk] |
design-as-text, smaller findings | S | yes | – | built offline (112de3a), live run passed 2026-10-31 |
| 2.4 | Outdated inputs run first; edakit run |
project-scripts block 4, decision 7 | M | yes | 1.4 | built (47ae793), live check passed 2026-10-31 |
| 3.1 | Checks in the gate: at, phase acceptance; then verify = script |
project-scripts block 6 | M | no | 1.2, 1.6 | done (9e63afd) |
| 3.2 | Project agents: [agents.*], replaces, their sign-offs required |
project-scripts block 7, decisions 10, 11 | M | no | 1.2 | done (8d3cc15) |
| 3.3 | Skills and the SessionStart hook use edakit scripts |
project-scripts block 8 | S–M | no | 2.4, 3.2 | done (9b4d834, 834f623) |
| 3.4 | Skill requirements, agent requirements-engineer, reviewers read the file |
requirements order 3, decision 7 | M | no | 1.6 | done (0099ee3) |
| 3.5 | req: as a source of value blocks |
requirements order 4 | S | no | 1.5 | done (abf40d4) |
| 3.6 | Own footprints as data in the parts entry | design-as-text gap 2, decision 5 | L | yes | 1.8 | built offline (8fd8e17), live run 2026-10-31: 3–6 hold, the box symbol does not suit the bell |
| 3.7 | Project setup: new-project with two ways, agent project-surveyor |
project-setup, decisions 1–6 | M | no | 3.2, 3.4 | done (92db89f) |
| 4.1 | Bell: requirements file | requirements order 5, decision 9 | M | no | 3.4, the bell's order | done in the bell (2026-11-01, on its main, its D-32) |
| 4.2 | Bell: entries for kunst and tdisplay |
project-scripts example | S | no | 2.4 | done in the bell (2026-11-01, on its main: [scripts.kunst] ×5, [scripts.tdisplay]) |
| 4.3 | Bell: the circuit as a language and as TOML with names, then the choice | design-as-text decision 8 | M | no | – | written (3f2b9e3, 7bbf3ce); reviewed and decided 2026-10-31: the language deferred, TOML with names the form; what both forms share has a plan of its own, circuit-language-plan.md, built 2026-10-31 (L.1–L.8; L.9, the bell converting, done there 2026-11-01); the language thought further in circuit-dsl.md |
| 4.4 | Tree: its positions, scripts, agents and requirements | all three | M | yes | the tree's move, 2.1, 3.7 | done in the tree (2026-10-31 and 2026-11-01, its D-50 to D-54, merged with pytree#34) |
| 4.5 | The requirements file becomes required | requirements decision 5 | S | no | 4.1, 4.4, requirements as inputs (#97) and the bell's file rewritten after it (its D-39) | done (2026-11-01, branch requirements-required): DOCS.file, open like a required sign-off – edakit check passes, --release and the gate do not; built before the bell's D-39 on the owner's word, the bell has its file |
| 5.0 | Probes: the makers' pages in the owner's browser, EasyEDA's ODB++ | ordering steps 0, 0b | – | yes (0b) | the owner's Chrome | done: step 0 (2026-10-31, branch makers-step0), 0b (2026-11-01, branch makers-step0b) |
| 5.1 | The order form as fields, order.md from them |
ordering step 1 | S | no | – | done (d58f4ae) |
| 5.2 | Every field of the form as a key, edakit's defaults, edakit cart fields |
ordering step 2 | M | no | 5.0, 5.1 | done (d58f4ae) – the keys of the reference orders; 5.0 adds what the forms show |
| 5.3 | Maker profiles; P-4, F-2 and F-6 per maker | ordering step 3 | M | no | 5.2 | done (d58f4ae); recorded by 5.0 on 2026-10-31 |
| 5.4 | edakit cart prepare and check (B-1 to B-7), the order record |
ordering step 4 | M | no | 5.3 | done (17446f9) |
| 5.5 | Agent regulatory-reviewer at the order | ordering step 5 | S | no | 5.4 | done (be8781d) |
| 5.6 | edakit cart actual: real cost, the fee table |
ordering step 6 | M | no | 5.4 | done (af59226) |
| 5.7 | Skill cart, live at JLCPCB up to the cart |
ordering step 7 | M | no | 5.0, 5.4, 5.5 | written (be8781d), live 2026-11-01 at JLCPCB into the cart, nothing ordered |
| 5.8 | BOM with MPN, P-12; Eurocircuits | ordering step 8 | M | no | 5.3, 5.7 | built offline (17446f9), Eurocircuits live 2026-11-01 into the basket (1.55 mm on the owner's word; the BOM's header Eurocircuits maps, the cart line's VAT) |
| 5.9 | new-project asks for the maker | ordering step 9 | S | no | 5.3, 5.8 | done (be8781d) |
| 5.10 | ODB++: MCP tool and bridge patch, F-7; AISLER | ordering step 10 | L | yes | 5.0, 5.7 | built offline (6b6ff5e), EasyEDA's export probed in 5.0 (0b), AISLER live 2026-11-01: up to the price on /checkout, every check passed (R1, R2 swapped in the throwaway copy for parts AISLER stocks) |
| 5.11 | The makers' interfaces, where granted | ordering step 11 | – | no | the makers | future work: wishes, nobody asks the makers (the owner's decision of 2026-11-01), handbook 8.4 |
| 5.12 | PCBWay: profile, its placeholder in F-6; live up to "Save to Cart" | ordering step 8b | S | no | 5.0, 5.7, 5.8 | profile and F-6 built (see below), live 2026-11-01: "Save to Cart" after the owner's "Agree", the files uploaded in the cart, nothing ordered |
S, M and L are the sizes the concepts give: small, medium, large.
Follow-ups the user decided on 2026-10-28, done: a draft requirement only warns and never blocks (1.6, b885376); the
order settings that change the made board join the fab hash (b5af0c6), and edakit cost names an order that deviates
from the board it prices (5a51877) (1.7). edakit cost no longer fails on a board nobody assembles (09f591d). Further,
on the user's word: the regulatory review says everywhere that it is support and not legal advice (0fddac4); python in
a project check is edakit's own (cfe053c); a missing sign-off file leaves the required checks open (2bb219f).
flowchart LR
subgraph P1["1 · offline"]
s11["1.1 README"]
s12["1.2 script entries, DAG"] --> s13["1.3 data names"] --> s14["1.4 stamps"]
s15["1.5 requirements file"] --> s16["1.6 requirement checks"]
s17["1.7 order settings"]
s18["1.8 geometry in the repo"]
end
subgraph P2["2 · with EasyEDA"]
s21["2.1 positions as text"]
s22["2.2 DRC rules"]
s23["2.3 designators"]
s24["2.4 scripts run"]
end
subgraph P3["3 · larger pieces"]
s31["3.1 checks in the gate"]
s32["3.2 project agents"]
s33["3.3 skills, session"]
s34["3.4 requirements engineer"]
s35["3.5 req: in value blocks"]
s36["3.6 footprints as data"]
s37["3.7 project setup"]
end
subgraph P4["4 · in the projects"]
s41["4.1 bell: requirements"]
s42["4.2 bell: scripts"]
s43["4.3 bell: language sketch"]
s44["4.4 tree moves"]
s45["4.5 requirements required"]
end
subgraph P5["5 · ordering"]
s50["5.0 probes"]
s51["5.1 form as fields"] --> s52["5.2 every field a key"] --> s53["5.3 makers"] --> s54["5.4 cart prepare, check"]
s54 --> s55["5.5 regulatory at the order"]
s54 --> s56["5.6 real cost"]
s57["5.7 skill cart"]
s58["5.8 MPN, Eurocircuits"]
s59["5.9 setup asks the maker"]
s510["5.10 ODB++, AISLER"]
s512["5.12 PCBWay"]
end
s14 --> s24 --> s33
s12 --> s31
s16 --> s31
s12 --> s32 --> s33
s16 --> s34 --> s41
s15 --> s35
s18 --> s36
s24 --> s42
s32 --> s37
s34 --> s37
s21 --> s44
s37 --> s44
s41 --> s45
s44 --> s45
s50 --> s52
s50 --> s57
s55 --> s57
s53 --> s58
s57 --> s58 --> s59
s50 --> s510
s57 --> s510
s58 --> s512
s17 --> s51
Phase 1: offline¶
1.1 README. The paragraph of design-as-text ("Circuit design as code. Everything a board is made of …") goes to
the head of README.md. "Status and limits" lists what is not text yet: the positions of the auto-layout (2.1), own
footprints (3.6), the DRC rule set (2.2), mask and print colour (1.7), single designators (2.3), and the two clicks
in EasyEDA. Each later step removes its line. Documentation only, so it may go to main at once.
1.2 Script entries. edakit/config.py reads [scripts.<name>] with the fields of the concept; a new module
resolves them together with edakit's fixed steps and reports R1–R9 in edakit check. [[release.stages]] and
sim_plugin count as the kinds check and sim, and the old keys keep working. Tests: one fault per rule, as the
concept's prototype did with 28 copies. The prototype is kept in
prototypes/project-scripts; its README names what the decisions changed since.
Its rules and cases become tests/test_scripts.py.
1.3 Data names. graph, parts, sim, board:<b>, footprints:<b>, routing:<b>, mech:<b>, gerber:<b>,
release and firmware, each with its hash; gerber:<b> takes fab_layers of the fab hash. To settle on the way:
the name for the project library that easyeda scripts change (the concept proposes project-library:<b>).
1.4 Stamps. hardware/scripts.lock holds per script the hashes of its reads and of the script itself at its
last run, as edakit sim stamps its result (inputs_sha256). A difference is a finding in edakit check and in the
gate, with the about sentence. edakit scripts lists every script with about and its state: current, outdated,
never run.
1.5 Requirements file. [project] requirements (default: none) names hardware/requirements.toml; a variant
names the main project's file and its own. The parser reads [[need]] and [[requirement]]: id, text, from,
binding, verify, status, reason. edakit req lists them with status and evidence; edakit req R-4 shows one
chain. The file covers only the circuit (decision 3). Test in led-bar with a small file.
1.6 Requirement checks. New catalog checks with add-rule: every reference resolves, every accepted need has a
requirement, every requirement has evidence, drafts warn. must without passing evidence blocks the release,
should warns, may informs. The gate protocol gets a section "Requirements"; accept steps join the acceptance
list of section 6 with their ID; the file joins the circuit hash byte for byte. The catalog letter for the new checks
must not be R or N, the prefixes of the requirement IDs. verify = script reports "not evaluated yet" until 3.1.
1.7 Order settings. [order] gets the colours of solder mask and silkscreen; the thickness comes from
thickness_mm, which feeds only the mechanical model today. F-6 and order.md name them.
1.8 Geometry in the repository. pcb_geometry_<b>.json moves from build/ to a versioned place, so CI and a
fresh clone can compute the layout hash. The path counts in the hash, so the layout hash changes once and sign-offs
with scope layout are recorded again. The bell's M-5 has scope fab (D-20) and keeps counting.
Phase 2: with EasyEDA¶
2.1 Positions as text. In this order:
edakit pcb autolayoutwrites position, rotation and side of every part it placed asauto = truerows into[place]of the board file, withwrite_place_rowsfrom #57 (decision 3).- A one-time takeover reads the positions of a board placed before from EasyEDA and writes them as fixed rows, without a new auto-layout.
- A check in stage L, as an error: every part of the graph has a row (decision 4).
led-barlacks D1 and R2 and gets them first; the bell has all 19 parts on all four boards.
The bell's session is told first, since board files change.
2.2 DRC rules. First a probe on a throwaway document of what PCB_Drc carries. Reading works
(getCurrentRuleConfiguration, with the bridge's return depth 12), and net rules can be written
(overwriteNetRules). If a whole rule set can be written, edakit writes it from the board file; otherwise it reads it
and checks it against the board file (decision 6).
Found without EasyEDA (handbook 9.4): the format, from the reference project's read of 2026-10-22 on 3.2.206
(tests/fixtures/easyeda/drc_rules_3.2.206.json); the signatures, from the client's pro-api/0.3.50…/api-types.d.ts;
and from its editor bundle that overwriteCurrentRuleConfiguration and overwriteNetRules put what they get into the
PCB's rules without a check and answer true, and that the net classes are the netClass entries of the net rules.
Built on the branch, for every project without effect until it opts in: edakit/drc_rules.py (the board file's rule
set, the differences to EasyEDA's, what edakit would write), eda.read_drc_rules and eda.write_drc_rules (the
second not run), and a third row of L-3 with [routing] drc_rules = "check".
The live probe, in one sitting with the EasyEDA claim; EasyEDA unthrottled, the patched bridge (edakit doctor):
- Throwaway document (agreement 18): a project
edakit-probe-drcmade by hand (File → New → Project), its PCB open and active. Three free pads put nets into it:PCB_PrimitivePad.create(1, "1", 0, 0, 0, ["ELLIPSE", 60, 60], "VIN", null, 0, 0, 0, false, 0), the same with"2",0, 200,"+5V"and"3",0, 400,"GND". Tracks of 10 mil on TOP (PCB_PrimitiveLine.create(net, 1, x1, y1, x2, y2, 10, false)): VIN 1000,0 → 2000,0 and +5V 1000,15.9 → 2000,15.9 – 0.150 mm apart; VIN 1000,300 → 2000,300; and on BOTTOM (layer 2) VIN 1000,500 → 2000,500. - Read and keep:
eda.read_drc_rules(or the threeeasyeda_api_calls) into the scratchpad as the restore point, andgetCurrentRuleConfigurationName,getAllRuleConfigurations(true)(the names),getDefaultRuleConfigurationName. Expected: no[MaxDepth], likely the name "JLCPCB Capability(Two Layers Board)" (the types' note for a PCB that never had other rules), the kinds and row/column lists of the recording, a Track–Track value below 0.15 mm.PCB_Drc.check(true, false, true)→ DRC A. - Write one value: the configuration with Track–Track of the default Safe Spacing rule at 0.2 mm,
overwriteCurrentRuleConfiguration(config)(the innerconfig) → true; read back: 0.2, the name unchanged, the named configurations unchanged; Design → Design Rule shows 0.2 mm. DRC B: a Clearance Error between VIN and +5V that A did not have – the DRC uses what was written, at once. - Kept: save, close, open the PCB again (
eda.reload_pcb), read back: still 0.2 mm (agreement 16). A second PCB of the throwaway project (made by hand) still reads the value of 2: the rules are the PCB's own. - Classes and track rules:
createNetClass("POWER", ["VIN", "+5V"], null)→ true;getNetRulesshows the class with VIN and +5V undersub– which rules do the moved nets keep? A Track rulePOWER(min 0.4572 mm, entries "1" and "2") throughdrc_rules.config_for, the net rules throughdrc_rules.net_rules_for, written as in 3. DRC C: a width finding on both 10-mil VIN tracks, TOP and BOTTOM. With only the entry "1" in the rule: is the BOTTOM track still found? (What a lone "1" covers.) Then save, reload, read back. - edakit's way: a board file in the scratchpad with led-bar's
[routing]and an edakit.toml naming the throwaway project;eda.write_drc_rules(mcp, drc_rules.wanted(board))→[];eda.reload_pcb;drc_rules.differenceson a new read →[]; DRC D: the board file's rules at work – the 10-mil VIN tracks found (class POWER, 18 mil), the gap of 0.150 mm found (clearance 6.5 mil = 0.165 mm). - Restore and clean up:
overwriteCurrentRuleConfigurationandoverwriteNetRuleswith the restore point,deleteNetClassfor what was made, read back = the restore point; the named configurations and the default name as in 2; the throwaway project deleted by hand.
What decides: if 3 and 4 hold and 5 and 6 hold, edakit writes the rule set (a value "write" of drc_rules: route
all writes it before its DRC; the gate goes on checking, it changes nothing). If the configuration holds (3, 4) but
classes or net rules do not (5), edakit writes the configuration and checks the classes. If a written value does not
survive the reload, the DRC ignores it, or it reaches another PCB or a named configuration, edakit only reads and checks
– the row of L-3 as built. The answer goes into handbook 9.4 and this step's line.
2.3 Single designators. A position per designator in [silk], as [place] has one per part; silk apply keeps
it.
2.4 Scripts run. pcb apply, silk apply and gate run the outdated input scripts they read first
(decision 7). edakit run <name> runs one script; an easyeda script runs only so, under the EasyEDA claim, and what
it changes makes the steps after it outdated.
Live runs of phase 2¶
2.1 to 2.4 are built and tested without EasyEDA; their EasyEDA part waits for a session with the user. Claim EasyEDA
first (edakit lock claim --note "live runs"), with EasyEDA unthrottled and the patched bridge (skill start), and
release it at the end. 2.2's probe is written out above.
2.1 Positions as text (pcb autolayout writes auto = true rows, pcb takeover takes a placed board, L-20).
Since 2026-10-30 edakit's own placer is to replace the auto-layout (kicad-toolchain.md,
T.1, the user's decision): run 2 matters only while pcb autolayout exists; 1, 3 and 4 – the takeover and L-20 – stay.
- On a scratch copy of the bell with all
[place]rows deleted:edakit pcb takeoverin main and invariants/esp32,smdandthtshows 19 rows per board, withoutauto, each equal to the real board file's row; nothing is written. - In a throwaway led-bar project (made by hand once) without the rows of D1 and R2:
schematic create --save,schematic layout --save,pcb parts --write,pcb dump,pcb apply(two parts unlocked), thenpcb autolayout. It writes twoauto = truerows at the end of[place]and nothing else; a second run writes nothing;pcb dumpandpcb applybring D1 and R2 back to their rows. - After
route all, delete every row but U1, U2, J1 and SW1:pcb takeovershows 13 rows,--writewrites them fixed,pcb applyleaves the parts on their tracks (pad check about 0),route drcfinds nothing new. edakit gate --easyedashows L-20 with every part in a row and L-7 passing.
2.3 Single designators ([silk.place]), on a throwaway or led-bar, e.g. R1 = { x = 59, y = 20, rot = 90 },
then edakit silk apply:
- R1's designator stands at the given spot and rotation, centred and visible; the summary says "fixed: R1"; the other designators and the board label keep clear of it.
- 90, 180 and 270 read as expected, and an angle such as 45 prints as the turned box.
- A row on the other side of its part gives a free text there (mirrored on the bottom), the part's own designator
is hidden, and a second
silk applyreplaces the text instead of doubling it. - A row on a pad prints the finding and exits 2; the Gerber check after the run reports the overlap.
edakit gate --easyedashows the expected L-17 rows; on a board withview = "bottom", check whetherrotreads right from below.
2.4 Scripts run (edakit run, inputs before the steps):
edakit run <easyeda script>in a Claude session:edakit lock statusnames the run, the script reaches EasyEDA under that claim, and a second session gets exit 3; a run over a minute keeps the claim.- Afterwards: delete the affected parts in the PCB,
pcb parts --write(recreated with the new footprint, tracks kept?),pcb dumpandroute all; the warning about the changed project library clears. pcb parts,pcb apply,route all,silk applyandgate --easyedawith an outdated input: the input runs before the EasyEDA work.
Results of 2026-10-31 (throwaway projects edakit-probe-ledbar, -drc, -fp; edakit of main at 8c4017a, EasyEDA Pro with the patched bridge 1.4.0):
- 2.1 passed. 1:
pcb takeoveron scratch copies of the bell (main, esp32, smd, tht) shows 19 rows each, withoutauto, each equal to the real row; nothing written. 2:pcb autolayoutwrites D1 and R2 asauto = truerows at the end of[place], a second run writes nothing ("every part … has a row"),pcb dump+pcb applyput both back on their rows to 0.001 mm. 3: afterroute all,takeovershows the 13 rows,--writewrites them fixed,pcb apply's pad check 0.004 mm,route drcnothing new. 4: the gate's SYNC.plan rows "every part has a row" (L-20) and "fixed parts = plan" (L-7) pass. - 2.2: writing holds. 2: no
[MaxDepth], the name "JLCPCB Capability(Two Layers Board)", Track–Track 0.102 mm,getDefaultRuleConfigurationNamenull; DRC A without a clearance error at 0.150 mm. 3: Track–Track 0.2 mm written (true), read back 0.2, DRC B finds VIN–+5V at once. The name changes to "自定义配置" (Custom Config), and that configuration appears ingetAllRuleConfigurations– saved in the client's user preferences (web.db, table users), so in every project's list; the named JLCPCB configurations keep their values. 4: kept after save and reload; a second PCB of the project keeps 0.102 mm – the rules are the PCB's. 5:createNetClasstrue, the moved nets keep "default" until the net rules are written; a Track rule POWER with entries "1" and "2" and with "1" alone both find the 10-mil track on BOTTOM – a lone "1" covers every copper layer; kept after reload. 6:eda.write_drc_ruleswith led-bar's[routing]→[], after reloaddifferences→[], DRC D finds the 18-mil class and the 0.165 mm clearance. 7: restored to the restore point value for value, classes deleted – but no API call puts a PCB back on a named configuration (PCB_Drchas none), anddeleteRuleConfiguration("自定义配置")answers true while a PCB uses it and the entry stays. By the plan's rule edakit writes the rule set:drc_rules = "write", built the same day (handbook 7.5); it leaves the PCB on the client's Custom Config, and a project that writes once stays on it. - 2.3 passed. 1: R1's designator at (59, 20), rot 90, centred and standing, "fixed: R1", the rest clear of it.
2: 180 upside down, 270 standing the other way, 45 slanted. 3:
side = "bottom"gives one free text R1 on the bottom silk, read right from below; R1's own designator hidden; a secondsilk applyreplaces it. 4: a row on R1's pad:silk previewandsilk applyreport "fixed designator on a pad (top) – 2.57 mm²", exit 2, the Gerber check 0.600 mm². 5: the gate's GEO.silk (L-17) row of edakit's print fails at R1.2, the rest as expected; withview = "bottom"rot = 90reads from below as 90 reads from above. - 2.4 passed. 1:
edakit run tdisplay(the bell's script as an easyeda script) runs under the claim; a second session gets exit 3 fromedakit run(exit 1 frompcb dump); a script of 75 s keeps the claim,lock statusnames "edakit run slow" and its last access moves on. Under a claim taken by handlock statuskeeps that claim's note. 2: after the change ofproject-library:esp32, deleting U1,pcb parts --writeandpcb dumpleave SYNC.scripts with "the routing" only; afterroute allthe warning is gone. 3: an outdated input (board:main) ran beforepcb parts,pcb apply,route all,silk applyandgate --easyeda, each time before the step's first line; the gate saw the new value. - Found on the way (fixes on their own branch):
schematic createandensure_boardright afteropenProjectsee no schematic or board yet (StopIteration, "no board") – a second run works; EasyEDA got into a state wheremodifySchematicNameanswered false, later true without effect, in every project, until EasyEDA was restarted;schematic createrefuses to save on a symbol whose pin names differ although the entry declares it (symbol_differsis read byedakit checkonly) – led-bar's Pico 2, whose EasyEDA symbol swaps SWDIO and SWCLK against the datasheet (41 SWCLK, 43 SWDIO); led-bar's plan has overlapping LEDs and U1 in a hole's keep-out (it never ran through EasyEDA); the local project's UUID is in~/Dokumente/EasyEDA-Pro/projects/<name>.eprj2(table projects), so edakit could find a project never opened.
Phase 3: the larger pieces¶
3.1 Checks in the gate. A check script gets its place (at: before S, or after F to see the run's Gerber data)
and its phase (export, order, acceptance); advisory only warns. With this, verify = script of the
requirements counts.
3.2 Project agents. [agents.<name>] with file, about, signs, replaces, scope, phase, after, and
the rules R10 and R11. The gate requires their sign-offs. With replaces, a project's agent signs M-1, M-5 or M-6 in
place of edakit's and takes over the round review too.
3.3 Skills and the session. start, pcb-round, hardware-change, release and new-project call
edakit scripts at their steps and start project agents at their hook points. The SessionStart hook (#74) lists the
scripts with their about.
3.4 Requirements engineer. The skill requirements collects in the conversation, asks back in a decision round,
writes drafts and has them approved. The agent requirements-engineer makes the inventory and gives a second opinion;
it changes nothing. circuit-design §1 reads the file. The electrical reviewer judges the requirements with
signoff = "M-1", the PCB reviewer those with M-5, the regulatory reviewer those with M-6. The plugin test that lets
the reviewer prompts name only catalog IDs learns N- and R-.
3.5 req: in value blocks. Documents in [docs] files quote a requirement by its ID, and G-19 finds a stale
quote (#71).
3.6 Footprints as data. The parts entry gets a footprint as data: origin, pads as part import caches them,
outline and print. A command builds the project's copy (a system part as carrier, then rebuilt), a check holds the
copy to the data, and a library footprint can become an own one. The data counts in the circuit hash, because M-1
checks polarity, and in the layout hash (decision 5). It would replace the bell's tdisplay_device.py and the tree's
led_footprints.py.
Built offline on its branch (handbook 4.5): footprint_data beside footprint in the parts entry; P-9 (the data
complete and consistent) and P-10 (the copy pcb dump measured equals the data), both in stage P and only where an
entry has the data; edakit part footprint KEY [--write] takes a measured footprint over as data; edakit part build
KEY [--write] builds the copy – the translation a pure function, the EasyEDA part (eda.find_copies,
eda.build_copy) tested on a fake and not run; pcb dump measures shape, size, rotation and layer of the pads of
parts with data (pcb.pad_shape, not run). The circuit hash covers the data without code; the layout hash adds it only
where it exists. Dry run on a scratch copy of the bell's esp32 variant: the T-Display as data from tdisplay_device.py
passes P-9 and P-10 against the bell's dump (0.0013 mm), the build's pad, polyline and string calls equal the script's
to 0.000 mil, one changed line fails P-10.
The live run, in one sitting with the EasyEDA claim (edakit lock claim --note "3.6 live run"), EasyEDA unthrottled
and the patched bridge (edakit doctor):
- Throwaway (agreement 18): a project
edakit-probe-fpmade by hand (File → New → Project); in the scratchpad a copy of the bell (edakit.tomlandhardware/of the main project and ofvariants/esp32), the esp32 variant's[project] name = "edakit-probe-fp", andC18722937.jsonwith the T-Display's data of the dry run – prototypes/footprint-data writes it into the copy. Add one pad to try a slot:"MH": {"x": 0, "y": 0, "shape": "OVAL", "w": 1.0, "h": 2.0, "hole": [0.6, 1.4], "plated": false, "mechanical": true}–edakit checkpasses P-9. - The carrier, not rebuilt:
schematic create --save,schematic layout --save,pcb parts --write,pcb dump,edakit check: P-10 fails on U1 – the header's pads, 2.54 mm between its rows, against the data's 22.86 mm. This shows that P-10 sees a copy that is not the data. - Build:
edakit part build C18722937prints the plan (25 pads, 2 lines, 2 texts, outline, symbol box);--writefinds the copies ongenerated_esp32, rebuilds and reads back – exit 0, no "read back" line,lock statusnames the claim. In EasyEDA's library view of the project: the footprint 2 × 12 at 22.86 mm with square pads 1 and 13, the outline, the USB box and both texts, the slot of MH upright (along h – the build turns a slot along the pad's longer side, an assumption until here); the symbol a box with 24 named pins. - Into the PCB: delete U1 in the PCB,
pcb parts --write,pcb dump: U1's pads inesp32.geometry.jsoncarryshape,w,h,rot,layerandhole[1.1, 1.1] (MH [0.6, 1.4], not plated);edakit check: P-10 passes, no note "not measured". If the shapes are missing, the bridge names the pad fields otherwise: read one pin ofgetAllPinsByPrimitiveIdand fixpcb.pad_shape.schematic check(S-2) passes on the box. - A pad as a line of text: pad 3
"y": -8.89→-8.79in the entry;edakit check– P-10 fails on pad 3 with both places, P-9 passes;part build --write, delete U1,pcb parts --write,pcb dump,edakit check– P-10 passes, the dump has pad 3 at −346.06 mil. - Kept (agreement 16): close and reopen the project; the library footprint and the placed U1 as built,
pcb dumpgives the same file. Optionallyedakit gate --easyeda: L-4 (the header's name kept), L-10 against the dump, L-8 and L-17 on the print, the drill file with 24 holes of 1.1 mm and the slot. - Clean up: delete the throwaway project by hand – the copies are its own, no library object stays.
What decides: if 3 to 6 hold, the data path replaces tdisplay_device.py – the bell's T-Display gets the dry run's
data, the script and the planned [scripts.tdisplay] go (step 4.2 keeps kunst), and the README drops "own
footprints and symbols"; the esp32 variant's circuit and layout hashes change once, so the bell's session is told
first. M-1 and M-6 (scope circuit) are then signed anew; M-5 (scope fab since the bell's D-20) still counts when the
esp32 gate exports the same files as the reviewed run, and its layout sign-offs M-2 and M-4 are open anyway. Whether a
footprint change should void M-6 at all is a question for the user (the bell's session, 2026-10-29): a footprint has
no regulatory content. If the copy is built but the box symbol does not suit the bell's schematic (the
script draws a box 120 wide, edakit one as wide as the names need, and schematic layout places it anew), the data
keeps the footprint and the symbol stays the script's: without "symbol": "box" the build leaves the carrier's symbol,
and tdisplay_device.py symbol remains an easyeda script. If the build does not hold – the editor opens another
document, the print is not replaced, the read-back or the reload differs –, the bell keeps its script, the data still
describes the footprint for P-9 and P-10, and the failing call goes into handbook 9.4.
Results of 2026-10-31 (throwaway project edakit-probe-fp, the bell's esp32 variant converted, [project] name the
throwaway): 1: P-9 (now PARTS.footprint) passes with the slot MH. 2: the carrier placed, SYNC.footprint (P-10) fails on
U1 – rows 2.54 mm apart against 22.86 mm, and pad size and hole (1.60/1.05 against 1.70/1.10 mm) –, so pcb dump
measures shape, size and hole. 3: part build plans 25 pads, 2 lines, 2 texts, outline, box; --write builds, exit
0, no read-back line; with [scripts] it records the change of project-library:esp32. 4: the parts must be placed
anew in the schematic – schematic create and schematic layout after the build, as the bell's README says for its
script; the hint of part build ("delete U1 in the PCB, pcb parts --write") leaves that out. Then pcb parts --write
creates U1, the dump has shape, w, h, rot, layer and hole [1.1, 1.1] (so PCB_PrimitivePad.create takes
holes in mil – B2), SYNC.footprint passes, schematic check passes. 5: pad 3 at −8.79: SYNC.footprint names both places,
PARTS.footprint passes; after the rebuild the dump has pad 3 at −346.05 mil and the check passes. 6: closed and reopened,
pcb dump gives the same file. The optional gate was left out.
Two things did not hold. The box symbol does not suit the bell's schematic: schematic layout with it fails
(wires not created, GND on U1.1 and U1.3–6) – the plan's fallback: the data keeps the footprint, the symbol stays the
script's (tdisplay_device.py symbol, without "symbol" in the data). PCB_PrimitiveComponent.create failed
("Cannot convert undefined or null to object") for U1 after the first builds – with the device of the project and of
the system library and with the footprint alone – while R1 was created; after a build without MH it worked, and then
with MH in all four forms (round and slot, plated or not) too. The cause is not found; edakit's message should name
the way out (schematic create, schematic layout, then pcb parts). The slot's direction in the library view and
the look of the footprint the owner checked in EasyEDA on 2026-10-31. The owner decided the same day that a changed footprint voids M-1, not M-6 (built: M-6 on the electrical hash). By the plan's rule the bell's footprint goes to the data
path and the symbol stays the script's – done in the bell's repository (its D-30, on its main 2026-11-01).
Phase 4: in the projects¶
Each at the user's request, in the project's repository.
4.1 Bell: requirements. After its order: the agent's inventory, a decision round (the servo supply, D-3 against the README), the file, only for the circuit. M-1 and M-6 open again, as decision 9 expects.
4.2 Bell: scripts. kunst for all four configurations and tdisplay for esp32, as in the concept's example.
4.3 Bell: language sketch. The whole bell written down twice, as a language of its own and as TOML with pin names; then the user chooses (decision 8). Building the language would be a step of its own, size L.
Written in circuit-language.md, with the prototype in
prototypes/circuit-language: bell.circuit and bell.toml by hand, and a
translator that takes all four configurations of the bell, led-bar and a graph with every field there and back without a
difference. The recommendation is the language; six decisions are open.
Reviewed on 2026-10-31 (the concept's section "Review 2026-10-31"): the owner deferred the language – it would need
abstraction, a schema that stands still and evidence from the evals to be worth its cost. Decided the same day, the
concept's table: TOML with names as the form, the pin map from the parts entry, the hash over the canonical data of
the form, a resolved graph.json generated beside it, four complete variants, the symbol's names and the roles, no
net in fragments. What both forms share has a plan of its own:
circuit-language-plan.md, built on 2026-10-31 up to L.8 – pins by name or role,
the graph as hardware/graph.toml, its hash, edakit graph json|convert, block add on TOML, the documentation –;
L.9, the bell converting, done in the bell on 2026-11-01 (on its main, edakit bc44fa7). The language itself is thought further in
circuit-dsl.md – what it would have to be, on which basis, when, and what toolchain it needs.
4.4 The tree moves. When the reference project moves to edakit after its order (EK-1 of its concept): the
takeover of its positions (2.1), seven of its eight scripts – review only reads the release (decision 8) – with
platine writing into tree.toml (decision 4), its agents elektrotechniker and pcb-pruefer with replaces, and
its requirements file. Done in pytree before its order instead (its EK-5 lifted EK-1): baumplan writes
tree.toml, the two agents went – edakit's reviewers signed M-1, M-5 and M-6 –, the requirements file accepted (D-51,
D-54).
4.5 Requirements required. Once the bell and the tree have a file, a project without one fails the gate (decision 5). Built 2026-11-01 as DOCS.file, severity pending: open like a required sign-off, so a project being set up still commits.
Phase 5: ordering at the manufacturer¶
Each at the user's request. The concept is ordering.md; its "Order of work" says what each step
builds, and its "Decisions" what the user decided on 2026-10-30: Claude fills the maker's form in the owner's browser
and stops at the cart – never orders, never pays –; the regulatory reviewer looks at every order; JLCPCB, PCBWay,
Eurocircuits or AISLER, asked when the project is set up; ODB++ from EasyEDA Pro; real cost into the project's fee
table. A project without [cart] stays as it is: its maker is JLCPCB, and every check runs as today.
5.0 Probes. The makers' pages in the owner's Chrome (file upload, read back, screenshots, the placement preview from the cart, labels and values for the profiles, test data) and the ODB++ export of a throwaway board under the EasyEDA claim. 5.2 waits for the field list, 5.7 and 5.10 for the probes.
5.1 to 5.6 are offline and tested on recorded data. 5.7, 5.8 and 5.10 are live, each up to the cart and nothing ordered. 5.10 changes the pinned MCP server and the bridge, as the schematic PDF did, with an issue and a pull request upstream.
Built on 2026-10-30, branch ccr-341cce07-y5or0n:
- 5.1–5.3 (d58f4ae):
order_sheet.form, every key of[order]with edakit's default,[order]of edakit.toml,order.jsonwith a new key only where it differs from the default,edakit/makers/(jlcpcb, eurocircuits, aisler) and[cart], F-6, P-4 and F-2 per maker,edakit cart fields. The led-bar'sedakit checkand offline gate report what they did before;order.mdlists every field with its source now. - 5.4, 5.8 offline (17446f9):
edakit cart prepare,edakit cart checkwith B-1 to B-7 (the letter B: K is a project's),--cartand the order record; the PCB price of a checked quote inedakit cost; the files of every maker andmakers.jsonin the gate;bom_<b>_mpn.csvand P-12. - 5.6 (af59226):
edakit cart actual, the project's fee table one to one,release currentwith the totals. - 5.5, 5.7, 5.9 (be8781d): the skill cart, the agent regulatory-reviewer "At the order", the topic maker of
new-project. 5.5 was tried on recorded data: the example ordered at JLCPCB for
salewith lead HASL and a page text that asks the assistant to keep the lead, add 50 boards and pay. The agent graded the finish and the page text block each, the import and the maker's material statements important, the parts' missing compliance a note, named the hashes of job and quote and ended withOrder: not OK; B-7 read the last line and the hashes and failed. Its check candidates, on the owner's word of 2026-11-01 (skill add-rule): lead graded by purpose already at the release – FILES.order/lead, as ORDER.finish grades it –, a BOM part withoutcompliance.rohs– PARTS.fact/rohs, a warning for a board that is sold –, the maker's RoHS and REACH statements in its profile –[compliance], read 2026-11-01, in the job for the agent; a page line that addresses the assistant stays the skill's and the agent's, not a finding ofedakit cart check– ORDER.form, ORDER.finish and the never list hold what such a line is after. -
5.10 offline (6b6ff5e):
easyeda_export_odbpatched into the pinned server bymcp-server.sh, the handler inbridge-extension-patch.sh(both applied to the files of 1.4.0 and parsed),edakit/odb.py, F-7 on an ODB++ the test writes from the example's export,edakit doctor, the upstream drafts (#5). -
5.12, the offline part (PCBWay, planned in by the user on 2026-10-30 after the rest):
edakit/makers/pcbway.toml, provisional – seat CN, USD, BOM with MPN (P-12), no catalogue, the placeholderWayWayWay, "Save to Cart" –, and F-6 finds the placeholder of another maker on the silkscreen, whichever it is (JLCPCB's at PCBWay, PCBWay's at JLCPCB); new-project offers it. -
JLCPCB's order number (the owner's decision of 2026-10-30, "Ad barcode – ok"): F-6 warns about the placeholder and
mark = "specify"while JLCPCB's profile reports the number gone (rules.placeholder_gone), andbarcodeandserial_numberare keys of[order]that change the board as made – no project that sets neither changes its fab hash; a board with the placeholder gets the warning to accept or the placeholder taken off. -
5.0, step 0b (2026-11-01, branch
makers-step0b): the bridge extension with ODB++ imported, a probe board (a copy of coin-cell-led, D1 turned by 90°, R1 on the bottom turned by 90°, BT1 kept out of the BOM) built in EasyEDA and exported througheasyeda_export_odband by hand. Both holdcomp_+_topandcomp_+_botwith package, pins and the device name (the MPN of an LCSC part, no LCSC number); EasyEDA turns clockwise (90° in the CPL is 270°, on the bottom too, mirror N) – F-7's assumption holds –; BT1 stays in the ODB++ without a mark, so a part withbom: falseis listed for the skill to mark (way b). SYNC.odb found 1.7 mm² at BT1's pads:Raster.renderdrew EasyEDA's RoundRect mask openings as obrounds. SYNC.odb now draws them as their rectangle (rr_box); the fab hash keeps its raster v1, so no signed hash changes. With that both exports pass SYNC.odb on every layer, hole, part and net; the run is a test fixture (tests/fixtures/makers/aisler/probe_2026-11-01, recordstep0b_2026-11-01.md).
The live runs of 5.8 and 5.10 ran on 2026-11-01 (tests/fixtures/makers/LIVE.md); 5.11 is future work (handbook 8.4).
Hashes and gate results¶
| Step | Circuit hash | Layout hash | Gate result |
|---|---|---|---|
| 1.6 | adds requirements.toml, where a project names it |
– | new rows; must without evidence blocks, only in projects with the file |
| 1.8 | – | changes once (the geometry's path) | – |
| 2.1 | – | changes when rows are written | a new error for parts without a row |
| 3.2 | – | – | a project agent's sign-off is required, where a project registers one |
| 3.6 | changes where footprint data is added | changes likewise | a new check: the copy equals the data |
| 4.5 | – | – | a missing requirements file blocks |
| 5.2 | – | changes where a board file sets a new key, as any edit of it; the fab hash where a key differs from edakit's default | F-6 checks every key |
| 5.3 | – | – | P-4 and F-2 run only with JLCPCB among the makers; a finding for a second maker warns; JLCPCB's placeholder on a board warns (its order number reported gone) |
| 5.8 | – | – | P-12, only with a maker that matches by MPN |
| 5.10 | – | – | F-7, only with a maker that takes ODB++ |
Working in parallel¶
Three tracks can run side by side, each in its own worktree:
- project scripts: 1.2 → 1.3 → 1.4 → 2.4 → 3.1, 3.2 → 3.3;
- requirements: 1.5 → 1.6 → 3.4 → 3.5;
- design as text: 1.1, 1.7, 1.8, 2.1–2.3, 3.6.
They meet in edakit/release/gate.py (1.6, 1.8, 3.1, 3.2, 4.5) and edakit/config.py (1.2, 1.5, 1.7, 2.3, 3.2).
Steps that share one of these files go to main one after the other, and the next one is rebased first.
Start¶
- 1.1, the README: documentation only, may go to main at once.
- 1.2 and 1.5 side by side on branches. They need neither EasyEDA nor the bell.
- Each step goes to main when it is ready; then 1.8 and 2.1.