Every artifact equals the graph and the plan – 10 codes.
Each engine decides one kind of relation; a finding's code is ENGINE.relation. Severities are the catalog's defaults – a project's domains may set others; a grade is a sub-case with its own severity, a topic the part of a code a domain switches on.
SYNC.schematic schematic = graph
error
Every schematic in EasyEDA has exactly the graph's parts (LCSC number, designator) and nets (pins, no-connects).
Why
The schematic is generated, but EasyEDA can lose or move things. The check reads the schematic as saved – its saved tabs closed and its pages opened again –, not as a session left it. Before easyeda-mcp-pro 1.9.0 the bridge's net reading could list a pin on a second net while another schematic of the project was open (M-11).
What to do
Generate it again (edakit schematic create --save, layout --save) and check again; never edit a schematic by hand. A schematic with unsaved changes: save it or close its tab without saving.
Sources
edakit handbook 6 and 13.2
Replaces S-2
SYNC.pcb PCB = graph
error
The PCB has exactly the graph's parts, every pad carries the graph's net, and every graph pin has a pad.
parts in the PCB = graph; every pad carries the graph's net; every graph pin has a pad – why it exists: the import may lose or mismatch parts
Why
Bringing parts into the PCB can lose, double or mismatch them, and a pad on the wrong net is routed wrong without any DRC error.
What to do
edakit pcb parts --write and edakit pcb nets --write.
Sources
edakit handbook 7 and 8.1
Replaces L-1
SYNC.footprint footprint = its source
errorunknown: note
Every part on the board has the footprint its entry names, every pad sits where the library footprint has the pad of its number (intended swaps in pad_map), and a copy built from footprint data equals that data (pads within 0.02 mm, holes within 0.01 mm).
footprint per part = parts database – why it exists: a swapped footprint variant
every pad sits where the library footprint has the pad with that number (pad_map for intended swaps) – why it exists: a pad swap that was reset by EasyEDA
the copy equals the footprint data: the footprint pcb dump measured on the board (<board>.geometry.json) has the entry's footprint name and the data's pads – the same numbers, each within 0.02 mm of its place (SYNC.plan's tolerance), the same hole within 0.01 mm, and where the dump measured them shape, size, rotation and layer; accept on the component. A part not dumped yet, or a dump without holes or shapes, is a note. In the gate SYNC.footprint holds the PCB's pads to the same dump (4.5) – finds e.g.: U1: the copy differs from the footprint data of C18722937 – pad 3 at (11.430, -8.891) instead of (11.430, -8.790) mm – a pad changed in the text and the copy in the PCB still the old one, found before anything is routed on it
Why
A footprint that differs from its source puts pads where the part has no pins.
What to do
Correct the footprint or the entry; an intended swap goes into pad_map.
Sources
edakit handbook 4.1
edakit handbook 4.4
edakit concept 'Circuit design as code' (docs/concepts/done/design-as-text.md), gap 2, proposal 3 ('a check holds the copy to the data: pins and pads in EasyEDA equal the parts entry'), and the implementation plan, step 3.6 and 'Hashes and gate results' ('a new check: the copy equals the data')
edakit handbook 9.4 ('Footprints in the project': placed parts keep their embedded copy until deleted and recreated); pytree's hardware/easyeda/led_footprints.py, bohrungen: changed pads of the placed part are dropped on saving, D31 stayed at 0.70 mm (2026-10-25)
0.02 mm: L-7's tolerance for a placed part against its plan (release/fab.py check_pcb); the Christmas bell's variants/esp32/hardware/boards/esp32.geometry.json has the T-Display's pads 0.05 mil (0.0013 mm) from where variants/esp32/hardware/lib/tdisplay_device.py created them, and handbook 7.4 has the library and EasyEDA Pro agreeing on the pads within 0.1 mil. 0.01 mm for holes and sizes: pcb dump keeps holes to 0.001 mm and pads to 0.01 mil – the step's choice, no standard was opened
The Christmas bell's variants/esp32/hardware/lib/tdisplay_device.py: its check reopens the rebuilt copy and compares pins and pad positions with what it built
Replaces L-4, L-10, P-10
SYNC.plan board = placement plan
error
Fixed parts sit where the plan says (±0.02 mm, rotation, side), the plan has no findings of its own, every part of a made board has a row in it, and it keeps what the circuit hands over (layout.fixed, edge, near, a capacitor next to every supply pin).
fixed parts in the PCB = plan (± 0.02 mm, rotation, side); the plan itself without findings – why it exists: a part moved by hand or by the auto-layout
the plan keeps the circuit's hand-over: layout.fixed, .edge, .near of the graph, a decoupling capacitor of its own next to every IC (7.4) – why it exists: the auto-layout put a decoupling capacitor 24 mm from its IC; what the circuit designer knew got lost on the way to the layout
on a hand-wired board with the plan of a perfboard (7.9): edge, near and the decoupling of the hand-over held as on a PCB, by the gap between the bodies' boxes, and [near] of its board file – why it exists: decision 20 of perfboard-layout
every part the graph puts on the board has a row in [place] of the board file – by hand, from pcb place, or written back by pcb autolayout with auto = true (7.4); a part on a bought or hand-wired board (4.5) is on no made board and needs none. The board file decides it alone; the row names the parts without one: D1, R2: no row in hardware/boards/main.toml [place] (2 of 17 parts) – why it exists: what EasyEDA's auto-layout placed was in no file: the layout hash did not cover it, SYNC.plan held it to nothing, the direction check, the mated plugs and the mechanical model did not see it, and after a dump the next auto-layout could put it elsewhere – pcb autolayout writes its positions back, pcb takeover --write takes a board placed before
Why
The plan is where mechanics and circuit meet; a part off its plan misses its hole, its edge or its capacitor.
What to do
Correct the plan or the placement as the message says.
Sources
edakit handbook 7
Analog Devices MT-101, Decoupling Techniques (Rev. 0, 2009) – https://www.analog.com/media/en/training-seminars/tutorials/MT-101.pdf
edakit handbook 7.4 and 14.1
edakit concept 'Circuit design as code' (docs/concepts/done/design-as-text.md), gap 1 and decisions 3 and 4 of 2026-10-28: the positions of the auto-layout into [place] with auto = true, the check an error in the gate, a board placed before takes over its positions once
edakit handbook 7.4 (the placement plan) and 8.1 (stage L)
Replaces L-7, L-12, L-20
SYNC.bom BOM = graph
error
The BOM generated from the graph equals the tool chain's BOM per designator – EasyEDA's, on the KiCad tool chain KiCad's –, and every LCSC number is in the parts database; parts with bom: false are left out on purpose.
BOM generated from the graph in JLCPCB format = the tool chain's BOM per designator (EasyEDA's, KiCad's on the KiCad tool chain); every LCSC number in the parts database; parts with bom: false left out on purpose
Why
JLCPCB assembles what the BOM says; a part that EasyEDA lists differently from the graph ends up differently on the board.
What to do
Correct the graph and bring the PCB in line (edakit pcb parts --write).
Sources
edakit handbook 8.1
Replaces F-1
SYNC.cpl pick and place = PCB
error
Every BOM part is in the pick-and-place file exactly once with the PCB's side and rotation, and only on the sides [release] assembly allows (Economic PCBA: one side, SMD only). A maker of [cart] makers whose zero orientation of a part is not the footprint's ([cpl] zero of its profile – Eurocircuits: IEC 61188-7) gets cpl_<board>_<maker>.csv, every part turned by its footprint's entry in [cpl.turns]; a footprint the profile does not list, or a turned part on the bottom, keeps its rotation and warns.
pick and place: every BOM part exactly once, side and rotation = PCB; sides and SMD-only as [release] assembly says (Economic PCBA: one side, SMD only; two SMD sides need Standard PCBA)
per maker with a zero orientation of its own: cpl_<board>_<maker>.csv, each part turned by [cpl.turns] of the profile (fnmatch on EasyEDA's footprint name); a footprint not listed, or a turned part on the bottom (the maker's bottom rule unread): its rotation as it is, a warning for that maker – M-2 checks it in the maker's preview. Not in the fab hash: cpl_<board>.csv is, the turns are the profile's
Why
A missing or doubled line is a missing or doubled part; a part on the wrong side needs Standard PCBA at a higher price. A maker that reads another zero orientation places a part turned (live run 5.8: Eurocircuits drew the SOT-23 Q1 turned by 90°).
What to do
Move parts to the allowed side in the plan, or mark hand-soldered parts bom: false. A footprint without a turn: check it in the maker's preview (M-2) and give it its line in [cpl.turns].
Sources
edakit handbook 8.4
Replaces F-3
SYNC.odb ODB++ = the released files
errorother_maker: warningunlisted: note
With a maker of [cart] makers that takes ODB++ (files = ["odb"] in its profile – AISLER), and with [release] maker_files = "all" (the default) on every board with an assembly side ([release] assembly sides, parts to place) as the shipped profile that takes it reads it: odb_<board>.zip, exported by EasyEDA through easyeda-mcp-pro's tool easyeda_export_odb or on the KiCad tool chain by kicad-cli pcb export odb, holds to this run's files – every board layer (copper, solder mask, paste, silkscreen) rasterised as GEO.copper does – EasyEDA's RoundRect mask openings as their rectangle, as the ODB++ has them – and XOR'd with the Gerber layer of the same side: no spot above 0.01 mm² of points farther than one raster point (0.02 mm) from the other layer – an edge that far off is the ODB++'s coarser coordinates (kicad-cli: 0.1 µm against the Gerber's 1 nm) –, no layer in one and not in the other; the drill hits = the drill files (place to 1 µm, diameter, plated or not); the profile = the Gerber outline (0.05 mm); every BOM part exactly once at the place (0.01 mm), rotation (ODB++ clockwise, the CPL counter-clockwise) and side of the CPL, with the part number the maker matches by (its profile's bom.match – AISLER: the MPN of the parts entry, which the KiCad tool chain writes into every footprint as MPN and Manufacturer; no match given: LCSC or MPN); a part with bom: false as the maker's profile says – left out (dnp = "omit"), else listed for the skill to mark; a tooling hole of the KiCad tool chain (a footprint with property edakit = "tooling") only marked .no_pop; the nets of the pins as the graph's, compared as groups of pins. An export that failed or cannot be read fails. A failure for [cart] maker, a warning for another maker, a note while no maker of [cart] makers takes ODB++ – the file is there to quote or order the release elsewhere, and the release does not depend on it.
Why
AISLER assembles from ODB++, not from Gerber, and no open-source tool turns the released Gerber, BOM and CPL into ODB++: the file comes from EasyEDA or KiCad, which also wrote the Gerber – but from its own state, at its own moment, through an export nobody else checks. The gate's checks hold the Gerber to the graph; SYNC.odb holds the ODB++ to the Gerber, so that what AISLER builds is what the gate released – without trusting the EDA program.
What to do
Parts without the number the maker matches by (AISLER: MPN): the parts entries need mpn and manufacturer (PARTS.fact/mpn), and on the KiCad tool chain edakit pcb apply writes them into the footprints of a board made before. Export again with the gate (edakit gate --eda): the ODB++ comes from the same state as the Gerber of the run. A layer that differs: the PCB changed between the exports, or EasyEDA's ODB++ export draws it otherwise – compare the spot named with the Gerber (edakit fab diff of two runs shows the Gerber side). An export that failed: on EasyEDA the MCP server and its bridge extension of the pinned version (edakit doctor, handbook 2.4 and 2.5), on KiCad the message of kicad-cli in odb_error.txt (handbook 16.6).
Sources
ODB++ Format Specification v8.1 (Siemens): matrix, features (L, P, A, S with OB/OS/OC/OE), standard symbols, components (CMP, TOP), eda/data (NET); rotation of components clockwise
AISLER: assembly from ODB++ or native design files, not from Gerber – https://community.aisler.net/t/introducing-odb-support/2459 (search result of 2026-10-30)
Live run 5.10 at AISLER (2026-11-01): the ODB++ carried LCSC numbers only; AISLER's "Auto detect MPN" assigned one part of ten (U1, whose value is its MPN), by the lcsc column none – AISLER knows no LCSC number, while its search had D1's APTD2012LSURCK and Q1's AO3415A
Live run 5.10 (the 555 blinker on the KiCad tool chain, 2026-11-01): kicad-cli's ODB++ has a track at y 17.4 that the Gerber file has at 17.400016, and a silkscreen line at 1.7 against 1.70005 – one raster row flipped along each, 1.06 mm² on a 53 mm line; tests/test_odb.py, test_an_edge_one_raster_step_off_is_rounding
Ordering at the manufacturer (docs/concepts/done/ordering.md, decided 2026-10-30): ODB++ from EasyEDA Pro, checked by SYNC.odb; the rotation convention and the part numbers in EasyEDA's export are confirmed by ordering step 0b
Feature proposal #153 (the Christmas bell, 2026-11-01): five boards released for JLCPCB only could not be quoted at AISLER from the release – no odb_<board>.zip without AISLER in [cart] makers, and only EasyEDA makes one; the owner decided: every assembled board gets it, held to the run as a note
Ordering step 0b (EasyEDA Pro 3.2.206, a probe board, 2026-11-01): the export through easyeda_export_odb and by hand (File > Export > ODB++) both hold component layers with package, pins and the device name (the MPN for LCSC parts, no LCSC number), turn clockwise (90° in the CPL is 270°, on the bottom too, mirror N), and keep a part kept out of EasyEDA's BOM without a mark; a rounded pad's mask opening is RoundRect (corners 0.1016 mm) in the Gerber and rect…xr2.0 in the ODB++ – 1.7 mm² of difference when the Gerber's was drawn as an obround; tests/fixtures/makers/aisler/step0b_2026-11-01.md, tests/test_odb.py, test_f7_on_easyeda_s_own_export
Replaces F-7
SYNC.source own part = its source
errormargin: warningstale: pendingunchecked: note
A board copied from another edakit project (own.what pcb, edakit part own) is that project's current datasheet and was copied from a commit, every datasheet a project writes (hardware/datasheet/) is of its current state, and a board that is not released at the state of its datasheet is a warning.
own parts (4.5): a board copied with edakit part own is its project's current datasheet, copied from a commit (pending otherwise); a board not released at that state is a warning; the project's own datasheets are of its current state – finds e.g.: a board changed after it was copied into the host
Why
An own board has no distributor and no manufacturer's datasheet: its parts entry is derived from the project that designed it – pins, supply range, current, compliance. When that project changes, the copy describes another board than the one that gets plugged in, and every check of the host reasons about the old one. A copy of uncommitted changes names no state anyone can find again. A board that has not passed its own gate is not checked as a product, whatever its datasheet says – that is the user's decision, so it is a warning to accept, not an error.
What to do
In the board's project: edakit part datasheet, then commit. In the host: edakit part own <path to the board's project>. Release the board there (edakit gate) and copy again, or accept the warning in the sign-off file with the reason.
Sources
edakit handbook 4.5
Replaces P-3
SYNC.scripts project scripts current
errormargin: warningunchecked: note
The project's scripts ([scripts.*] with [[release.stages]] and sim_plugin) resolve with edakit's steps into one order, and every script whose inputs changed ran again (hardware/scripts.lock).
Why
A derived file from before a change describes the old circuit.
What to do
edakit run <name> runs a script that is outdated or never ran and records the run (handbook 8.6).
Sources
edakit handbook 8.6
SYNC.agents project agents complete
error
Every project agent ([agents.*]) has its agent file, signs only what is its to sign, and names in after only agents that exist.
Why
A sign-off by an agent nobody can run is a sign-off by nobody.
What to do
Correct the entry in edakit.toml or add the agent file (handbook 8.6).