master
AdityaAngajala Merge PR #261: Require and verify molecule fabrication sources 7227f5f 1d ago

Fabrication sources

Every new KiCad, Fusion or Altium molecule export includes <slug>-fab.zip and export.json. Make them from the same saved board revision as the STEP. Finish source edits, including custom-model path rewrites, before exporting. Use a fresh folder per export so old files cannot enter a new bundle.

The zip contains both sides' copper, solder mask and silkscreen, the actual board outline, and Excellon drill files. Keep plated and unplated drills separate where the exporter supports it; record mixed drills honestly. Keep the original file bytes and names inside the zip. Optional inner copper, paste and Gerber job files may travel too. Preserve silk PNGs while the current converter still consumes them.

export.json uses the existing adom.export/1 contract from Fusion PR #17:

  • tool, toolVersion, exporter, exporterVersion: actual producers.
  • files[]: relative path, role, byte count and SHA-256 for the STEP, saved board, schematic sheets and every additional source handed to publishing.
  • The fab_zip file has entries[], each with its relative path inside the zip, role, byte count and SHA-256. Nested entries avoid confusing archive members with real page paths.
  • Layer roles: copper_top, copper_bottom, mask_top, mask_bottom, silk_top, silk_bottom, outline; drill roles: drill_plated, drill_unplated, drill_mixed; optional paste_top, paste_bottom, copper_inner_1 (and subsequent inner layers), gerber_job and other.
  • Source roles: step, board_kicad_pcb, board_eagle_brd, board_altium_pcbdoc, schematic, project, and descriptive roles for optional sources (for example fusion_archive, usdz, bom, cpl).
  • stackup: known thicknessMm, copperLayers, maskColor and silkColor by top/bottom, and finish. Unknown values stay null or absent; never turn a viewer's default into claimed design data.
  • gerbers.included is true. drill.plating says split or mixed. Exporters may also preserve origin, outline, exportedAt and their own provenance. A historical backfill carries backfill and its original tier; it must never claim to be a fresh native EDA export.

The helper in this package is Python 3.9+ with no third-party dependencies. Run it from the package directory, with the export folder as its argument:

python3 scripts/fab_bundle.py create /path/to/bundle --slug my-board \
  --fab /path/to/fresh-gerbers --tool KiCad --tool-version 10.0.3 \
  --exporter adom/kicad-export-molecule --exporter-version 1.2.0 \
  --source step=Board.step --source board_kicad_pcb=Board.kicad_pcb \
  --source schematic=Board.kicad_sch --source project=Board.kicad_pro \
  --stackup /path/to/known-stackup.json
python3 scripts/fab_bundle.py check /path/to/bundle --slug my-board

Use the versions actually installed, not the example values. --fab also accepts an EDA-produced zip. --stackup is optional; pass the data the EDA provides. Repeat --source for every exported source, including every schematic sheet. The helper writes the canonical zip name without changing its members' bytes. It refuses missing layers, a blank outline, missing drill data and changed hashes. It does not prove alignment or validate the circuit; the existing conversion and manufacturing checks still apply.

For a bridge-produced record, verify it before any packaging rename. If the bridge used a design stem instead of the wiki slug, use a fresh staging folder and regenerate the record against the final filenames. Never rename a recorded source or zip without updating and checking the record.

Before publishing, run check after all local source changes. Include the zip, record and every recorded source in both the package and the explicit repo push list. Re-fetch those files at the returned commit into a fresh folder, then run the same check there. A successful push response alone does not prove that the complete export reached the page.

This contract applies to EDA molecule exports. tscircuit retains circuit.json and its build inputs; its pipeline generates fabrication layers. Existing pages are backfilled separately. This source addition does not enable registry model serving or replace any page's current model.