Skip to content

Project template — one repo per product, rules baked in

Status: proposal — awaiting decision, 2026-09-14 (task 150). Plan started 2026-09-13.

The decision asked of Engineering: adopt the safe-everywhere design-rule set as the KiCad template default (0.20/0.20 track/space, 0.30 drill, 0.15 annular ring, 0.30 copper-to-edge, 0.15 silk, 0.10 mask dam, 1 oz, no impedance assumed) so any board can go to any of six fabs unchanged, keep the six existing net classes, and add a second JLCPCB-only .kicad_dru for boards that need finer geometry. Numbers and sources are in "Fab capabilities" below; two items still need an engineer's eye (JLCPCB's 0.25 mm figure, and the USB 90 Ω width/gap from JLCPCB's impedance calculator). Approve, amend the table, or reject on the home page. Angel's ask: a template KiCad repo carrying our rules (net classes, trace widths, clearances), an SOP for starting a new PCB, and an answer to the harder question — a product is not just a board. It has an enclosure, firmware, an app, labels, artwork. How is that captured?

What the Real Cow teaches

The Real Cow exists across five repos: Cow_Robot_Board, Cow_Remote, Cow_LCD, The_Real_Cow_Flasher, The_Real_Cow_Configurator. Each is sensible alone. Together they have no front door: nothing says what the product is, which board revision ships with which firmware, or where the enclosure lives. Answering "what did we ship?" means knowing the history.

What it got right, and the template should keep: Cow_Robot_Board already holds hardware/, firmware/, FreeCad/ and Solidworks/ side by side. The shop's instinct was already one-repo-per-thing-you-build. The drift was the remote, the LCD and the two apps becoming their own islands.

The decision: one repo per product

At two people, the coordination cost of five repos beats the coupling cost of one — badly. A product gets one repo, with a fixed shape:

product.yml            the manifest — what this is, and where every piece lives
hardware/<board>/      one KiCad project per board, from the template
firmware/<target>/     one per MCU
app/                   configurator, mobile, whatever the customer touches
mechanical/            enclosure, CAD source and exported STEP/STL
artwork/               labels, silkscreen art, packaging, the printed insert
test/                  fixtures, test plans, and the Universal Tester plan (TOML)
docs/                  the datasheet, the manual, compliance evidence
.github/workflows/     the checks

Where a piece genuinely must live elsewhere — a public configurator, a fork of somebody else's flasher — it stays where it is and product.yml points at it. The manifest is what makes a product findable even when it is not all in one place. That is the whole trick, and it is why the manifest matters more than the folder layout.

product.yml

The one file that makes a scattered product coherent, and the thing the engineering portal reads (engineering-portal):

product: The Real Cow
ipn: EE-P-0007              # the ERP part number — the ERP owns the number (SOP-001)
status: shipping            # concept | prototype | shipping | retired
revision: C                 # the PRODUCT revision, not the board's
boards:
  - name: Robot Cow
    path: hardware/robot-cow
    revision: 4.0.1
    ipn: EE-A-0031
firmware:
  - target: STM32G0
    path: firmware/robot-cow
    builds_for: [Robot Cow 4.0.1]
mechanical: mechanical/enclosure-v3
artwork: artwork/
external:
  - what: Configurator (public, browser)
    repo: EmbeddedEra/The_Real_Cow_Configurator
compliance: docs/compliance/     # FCC IDs, UN38.3, RoHS — see embeddedera-business

Why a manifest rather than a README: a person reads a README; the portal, the audit and the ERP sync read a manifest. It is checkable — "every board has an IPN", "status shipping implies compliance evidence exists" become audit rules the same way SOP checks are.

The KiCad template, with the rules in it

hardware/<board>/ starts as a copy of the template already in the KiCad repo (template/EmbeddedEra_Project), extended so a new board is correct by default rather than correct if you remember:

  • Net classes — Default, Power, HV, USB (90 Ω differential), RF (50 Ω), and a documented trace width, clearance and via size for each.
  • A .kicad_dru custom rules file for the things net classes cannot say: annular ring minimums, edge clearance, courtyard overlap, silkscreen over pad.
  • A stackup that matches what we order — JLCPCB 2- and 4-layer are already separate templates; keep that and name the fab in the template's name, because the rules are the fab's, not ours.
  • Sheet template and title block already filled with the shop's details.
  • The ERC/DRC severities we mean, saved in the project, so the checks in CI and the checks in the editor agree.

Where the numbers come from: whatever JLCPCB's capability page says for the process we actually buy, not folklore. Writing this down is part of the work, and it is the part that needs an engineer rather than me.

SOPs

  • SOP-021 New PCB Design — start from the template, name and number it (SOP-001), the net classes and when to deviate, when a review happens, what "done" means (ERC and DRC clean, BOM resolves to real IPNs, fab files generated by CI not by hand).
  • SOP-022 Product Records — what a product repo must contain, product.yml, how a revision is cut, and what evidence a shipping product must carry.

Both cross-reference SOP-008 KiCad Library and SOP-001 Part Numbering instead of restating them.

Order

  1. Write the rules down (net classes, widths, clearances) — needs the fab's capabilities.
  2. Extend the KiCad project template with those rules and the DRU file.
  3. Create EmbeddedEra/Project_Template with the folder shape, product.yml, and the checks workflow already wired.
  4. SOP-021, then SOP-022. (018 Content Publishing, 019 Cloudflare Changes and 020 Credentials were written on 2026-09-14 — numbers are sequential and never reused.)
  5. Backfill product.yml for the Real Cow across its five repos — the test of whether the manifest idea actually works is whether it can describe the messy real case.
  6. The portal reads the manifests (engineering-portal).

Related: KiCad, engineering-portal, task-board, universal-tester-product.

Fab capabilities (research, 2026-09-14)

Scouted from the fabs' own capability pages by a research agent; every number carries its URL below. Cells marked unverified were not on an official page. Not yet checked by an engineer — that check is task 150. Two known weak spots to verify first: the agent labelled JLCPCB's 0.25 mm figure "annular ring" but it is more likely the minimum via pad diameter (0.15 drill + 0.05 ring each side), and it did not find JLCPCB's impedance calculator (it exists at jlcpcb.com/pcb-impedance-calculator; the stackup codes such as JLC04161H-7628 are shown there, not on the capabilities page).

Fab Track/Space 2L Track/Space 4L Min drill Annular ring Hole-to-Cu Hole-to-hole Cu-to-edge Silk width Mask dam Thickness Copper ENIG Impedance
JLCPCB 0.10/0.10 0.09/0.09 0.15 0.25 (see note) ~0.2 unverified ≥0.2 ≥0.15 0.10 0.4–2.0 1–4.5 oz out, 0.5–2 in yes ±10 %, 4+ layers
PCBWay 0.10/0.10 0.10/0.10 0.15 0.15 ~0.1 unverified 0.25 0.15 0.05 0.2–3.2 1–8 oz out, 1–4 in yes ±10 %
NextPCB 0.08/0.08 0.08/0.08 0.15 ≥0.1 ~0.1 unverified ≥0.2 ≥0.12 ≥0.04 0.6–3.2 1–2 oz out, 0.5–2 in unverified ±10 %
Seeed Fusion 0.15/0.15 0.15/0.15 0.3 unverified unverified unverified unverified unverified 0.1 0.8–2.0 1 oz out yes (+$10) unverified
Elecrow 0.20/0.20 0.20/0.20 0.3 0.15 0.3 0.45 unverified 0.15 unverified unverified 1 oz all unverified no
ALLPCB 0.10/0.10 0.10/0.10 0.2 ≥0.153 unverified unverified ≥0.3 ≥0.15 unverified 0.4–3.2 1–2 oz out, 0.5–2 in yes yes, details unverified
Safe everywhere 0.20/0.20 0.20/0.20 0.30 0.15 0.30 0.45 0.30 0.15 0.10 0.8–2.0 1 oz yes do not assume

All mm. "Safe everywhere" is the tightest rule set that passes every fab listed; it is also, not by accident, almost exactly what the KiCad template already uses (0.20 track, 0.20 clearance, 0.60/0.30 via, 0.5 edge). The design-rule decision is therefore: ship the safe set as the template default (a board can go to any of six fabs unchanged) and keep a JLCPCB .kicad_dru for the boards that need the headroom (USB-C, fine-pitch BGA).

Assembly: JLCPCB in-house library + consigned, 0201 yes, BGA 0.35 mm, 2–3 days; PCBWay 0201 risky, BGA 0.25 mm with X-ray, 8–48 h; NextPCB BGA 0.5 mm; Seeed 0402 minimum, 4–6 days; Elecrow 0201 claimed. Price bands were not found on official pages (unverified).

Sources: JLCPCB https://jlcpcb.com/capabilities/pcb-capabilities and https://jlcpcb.com/capabilities/pcb-assembly-capabilities · PCBWay https://www.pcbway.com/capabilities.html, https://m.pcbway.com/assembly-capabilities.html · NextPCB https://www.nextpcb.com/pcb-capabilities · Seeed https://wiki.seeedstudio.com/Service_for_Fusion_PCB/ · Elecrow https://www.elecrow.com/blog/a-complete-guide-from-kicad-design-to-pcb-prototyping-order.html · ALLPCB https://www.allpcb.com/standard_pcb_manufacturing_capability.html

Rule files to fork (no fab publishes an official .kicad_dru): Cimos/KiCad-CustomDesignRules (JLCPCB + PCBWay, KiCad 8), labtroll/KiCad-DesignRules (JLCPCB, KiCad 7), Huaqiu-Electronics/NextPCB-Manufacturing-Rules (NextPCB).

Build plan for task 151 (2026-09-14)

What already exists, checked before planning (Hard Rule 1):

  • The KiCad template already carries six net classes (kicad-lib/template/EmbeddedEra_Project: Default 0.20, Signal_Robust 0.25, Power_500mA 0.40, Power_1A 0.60, Power_2A 1.00, USB2 0.20 pair) plus naming conventions, README and AGENTS.md — the Universal Tester 003 repo's EMBEDDEDERA_TEMPLATE_UPDATE_PLAN.md did that work and marks it complete. Task 150 is therefore smaller than it reads: not "invent the classes" but "confirm the fab minimums and write them into a .kicad_dru". UT003's PCB_NET_CONSTRAINTS_PLAN.md already used JLCPCB-derived values (0.1375 mm clearance, 0.20 mm min track, 0.60/0.30 via) and is the best starting point.
  • Universal_Tester_003 is already the target shape (hardware/kicad, hardware/enclosure, firmware/, host/, docs/, README with a layout table). The template should look like UT003 tidied up, not like something new; Cow_Robot_Board (hardware/<rev>/, firmware/, FreeCad/, Solidworks/) is the second reference.
  • EmbeddedEra-products/templates/product-template + new_product.py is a different idea (the modular-ECU merge workspace with fusion/modules submodules). Leave it alone; it is not a per-product repo template and its copy-and-rename script is replaced by GitHub's own template feature below.
  • CI: kicad-lib/.github/workflows/kicad-checks.yml (KiCad 10.0.5 container + kicad-cli, written 2026-09-13, not yet pushed — task 100) is the check to reuse verbatim. Community alternatives were checked: KiBot (739★, community default, but KiCad 10 support limited as of v1.9.1) and actions-for-kicad/kicad-actions (25★, KiCad 10 ok). Neither is needed: kicad-cli already exports gerbers, drill, BOM and position files, so fab outputs are a second job in the same workflow, no extra tool. Revisit KiBot only if we want the PDF/3D render set.
  • Manifest: Open Know-How (OKH v1, iop-alliance, active) is the published standard for describing open hardware, but it models one design, not "board X rev 4.0.1 ships with firmware Y". Decision: keep product.yml, but use OKH's field names wherever they overlap (title, version, license, repo, documentation-home, bom) so an OKH export is a mapping, not a rewrite. CycloneDX HBOM is for supply-chain SBOMs, not this.
  • Design-rule files: labtroll/KiCad-DesignRules (362★, JLCPCB, KiCad 7 syntax) and tinfever/KiCAD-Custom-DRC-Rules-for-JLCPCB-with-Unit-Tests (4-layer, with test boards) are the references to fork; .kicad_dru is native in KiCad 10 with a graphical editor, so the file ports.

Steps

  1. Task 150 — design rules (engineer, ~1 h, blocks the rest). Open JLCPCB's capability page for the 2-layer 1 oz and 4-layer processes we buy and fill one table: min track/space, min annular ring, min drill, min hole-to-copper, edge clearance, silkscreen minimums, USB 90 Ω width/gap for each stackup (JLC's impedance calculator, JLC04161H-7628 for 4-layer). Decide whether the six existing classes stay. Output: the table in this note. Claude then writes EmbeddedEra_JLCPCB_2L.kicad_dru and _4L.kicad_dru from labtroll/tinfever as the base, sets the board minimums in the two template .kicad_pro files, and runs kicad-check on the template to prove it is DRC-clean out of the box.
  2. Create EmbeddedEra/Project_Template (Claude, no engineer needed). Private repo, marked a GitHub template repository (gh repo edit --template), so a product starts with gh repo create EmbeddedEra/<Product> --template EmbeddedEra/Project_Template --private and the copy-rename script goes away. Contents:
  3. product.yml with every field present and a # required / # optional comment, plus docs/product-yml.md documenting each field.
  4. hardware/board-1/ = a copy of the KiCad template (post step 1); firmware/, app/, mechanical/, artwork/, test/, docs/ each with a one-paragraph README saying what goes there and what must not (no generated fab files, no backups, no .DS_Store).
  5. .gitignore and .gitattributes merged from UT003 and kicad-lib (KiCad backups, -cache, fp-info-cache, *.bak, STEP exports as LFS).
  6. .github/workflows/kicad-checks.yml copied from kicad-lib, with the library tables step changed to clone EmbeddedEra/KiCad at a pinned tag (a product repo has no libraries of its own); a second job fab-outputs that on a tag hw/<board>/<rev> runs kicad-cli pcb export gerbers|drill|pos and sch export bom, zips them, attaches them to the GitHub release.
  7. .github/workflows/manifest-check.yml: a 40-line Python check that product.yml parses, every boards[].path exists and contains a .kicad_pro, every board has an ipn, and status: shipping implies docs/compliance/ is non-empty. This is what makes the manifest auditable (SOP-022 later cites this check).
  8. AGENTS.md from the KiCad template, extended with the repo shape.
  9. README with the layout table (UT003 style) and the "start a product" steps.
  10. Prove it on a fresh clone: create a throwaway repo from the template, push, watch both workflows go green, delete the throwaway. Then push task 100's workflow to kicad-lib the same day so the two repos run the same checks.
  11. SOP-021 New PCB Design, SOP-022 Product Records (task 152) — written against the real template, not before it. SOP-021 is mostly "use the template; deviate from a net class only with a comment in the schematic"; SOP-022 is the product.yml contract and the revision cut (tag hw/<board>/<rev> = fab outputs frozen).
  12. Real Cow backfill (task 153): one product.yml in Cow_Robot_Board pointing at the other four repos through external:. If the manifest cannot describe it, the schema is wrong and gets fixed before SOP-022 is published. Second test: Universal_Tester_003, which should need only the manifest and a workflow.
  13. Add a tile for the engineering portal reading manifests later (engineering-portal).

What needs Angel

  • Step 1 only: the fab numbers and a yes/no on the six classes. Everything after is Claude's, each step verified on a real clone before being called done.

Log

  • 2026-09-13: written, after looking at how the Real Cow is spread across five repos.
  • 2026-09-14: build plan for task 151 written after checking existing work (KiCad template already has the net classes; UT003 is the reference shape; GitHub template repos replace the copy script; OKH field names, not OKH itself; kicad-cli, not KiBot, for CI).
  • 2026-09-14: fab capability table added from a research pass over six Asian fabs; the safe-everywhere set matches the template's current numbers. Engineer check pending (task 150).