@protoboardai/ext-tscircuit 0.0.0-stage → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +59 -0
- package/README.md +493 -2
- package/evals/announce-pcb-fallback/case.ts +67 -0
- package/evals/catch-swapped-nets/case.ts +69 -0
- package/evals/generate-imu-board/case.ts +60 -0
- package/evals/locked-board-fix-upstream/case.ts +70 -0
- package/lib/checks.ts +464 -0
- package/lib/config.ts +384 -0
- package/lib/fingerprint.ts +144 -0
- package/lib/footprints.ts +370 -0
- package/lib/kicad/cli.ts +93 -0
- package/lib/kicad/drc.ts +95 -0
- package/lib/kicad/fields.ts +65 -0
- package/lib/kicad/handoff.ts +112 -0
- package/lib/kicad/netlists.ts +311 -0
- package/lib/kicad/probe.cjs +21 -0
- package/lib/kicad/project.ts +594 -0
- package/lib/kicad/refdes.ts +130 -0
- package/lib/kicad/schematic.ts +624 -0
- package/lib/kicad/sexpr.ts +117 -0
- package/lib/land-geometry.ts +177 -0
- package/lib/land-patterns.ts +706 -0
- package/lib/layout.ts +236 -0
- package/lib/models.ts +284 -0
- package/lib/native.ts +199 -0
- package/lib/outputs.ts +383 -0
- package/lib/paste.ts +69 -0
- package/lib/probe.cjs +31 -0
- package/lib/runtime.ts +185 -0
- package/lib/silk.ts +141 -0
- package/lib/source.ts +472 -0
- package/lib/stepfile.ts +210 -0
- package/lib/suppliers.ts +39 -0
- package/ops/circuit.ts +270 -0
- package/ops/kicad.ts +636 -0
- package/ops/package.ts +156 -0
- package/package.json +81 -4
- package/protoboard-extension.json +313 -0
- package/skills/circuit-board/SKILL.md +494 -0
- package/skills/circuit-board/references/pcb-workflow.md +87 -0
- package/skills/kicad-cli/SKILL.md +210 -0
- package/skills/kicad-layout-review/SKILL.md +275 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: circuit-board
|
|
3
|
+
description: Design a custom PCB through the circuit capability — the board as a UHD module (components as children, nets as links, its edge as exports), a circuit.config.ts naming @protoboardai/ext-tscircuit and its constraints (outline, layers, mounting holes, fixed placements, keep-outs), then protoboard run circuit:generate / circuit:export / circuit:extract / circuit:check --module <board> for a routed board and its standard outputs (Gerber, drill, BOM, pick-and-place, GLB, STEP, geometry manifest, netlist), and fix what the check finds (DRC, netlist drift, unrouted nets, outline, keep-outs, moved placements). This is the only sanctioned PCB path in a Protoboard project: never run tsci dev/build/export on a stack board; the fallback to tsci or KiCad tooling, when the official path fails and a board is needed anyway, is announced and recorded first. A KiCad 10 preview (circuit:kicad) and, when a person decides, the handoff to KiCad (circuit:lock / circuit:unlock). Use when a project needs a custom board, when the board module or its config changes, or when CIRCUIT_* diagnostics fire.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Circuit board
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
board module (components, nets, exports) + circuit.config.ts (outline, layers, placements, keep-outs)
|
|
10
|
+
│ protoboard run circuit:generate --module <board>
|
|
11
|
+
▼
|
|
12
|
+
tscircuit/board.gen.tsx ─► local autorouter ─► tscircuit/circuit.json, build.json (native design)
|
|
13
|
+
│ protoboard run circuit:export --module <board>
|
|
14
|
+
▼
|
|
15
|
+
out/gerber/, bom.csv, pnp.csv, board.glb, board.step, board.manifest.json, netlist.json (standard outputs)
|
|
16
|
+
│ protoboard run circuit:extract --module <board> → frames for the enclosure
|
|
17
|
+
└─ circuit:check (in protoboard check): DRC, copper netlist vs UHD nets, outline, keep-outs, placements
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The board is UHD; the PCB is the provider's. The board module says what is
|
|
21
|
+
on the board and how it is connected, in tool-neutral terms. The provider,
|
|
22
|
+
`@protoboardai/ext-tscircuit`, owns land patterns, placement and routing, in
|
|
23
|
+
its own folder next to the module. Swapping to another `circuit` provider is
|
|
24
|
+
an edit to `circuit.config.ts`. Full reference: the package README
|
|
25
|
+
(`node_modules/@protoboardai/ext-tscircuit/README.md`); complete example:
|
|
26
|
+
`packages/ext-tscircuit/test/fixtures/imu-tile` in the stack repository (an
|
|
27
|
+
RP2040, a BMI270 and a TPS63020 on a 30 mm round board).
|
|
28
|
+
|
|
29
|
+
**The official path** is the stack's: model the board in UHD, then
|
|
30
|
+
`circuit:generate`, `circuit:check`, `circuit:export`, and view it in VS Code
|
|
31
|
+
(the Protoboard viewer). Never run `tsci dev`, `tsci build` or `tsci export` on
|
|
32
|
+
a stack board or at the project root: tsci rebuilds the board with another
|
|
33
|
+
tscircuit core, re-routes it, and crashes on the files the provider rewrites
|
|
34
|
+
(as happened on a large field project), and its outputs escape every check. The flow
|
|
35
|
+
is UHD → tscircuit → KiCad: tscircuit while the design changes, KiCad for the
|
|
36
|
+
final hand placement once a person locks the design (step 8: `circuit:kicad`,
|
|
37
|
+
`circuit:lock`, `circuit:unlock`; after lock the **kicad-layout-review** and
|
|
38
|
+
**kicad-cli** skills). Why, and the fallback policy:
|
|
39
|
+
[references/pcb-workflow.md](references/pcb-workflow.md).
|
|
40
|
+
|
|
41
|
+
## Purpose
|
|
42
|
+
|
|
43
|
+
Turn a UHD board module into a routed PCB and its standard outputs, and keep
|
|
44
|
+
the copper and the model in agreement. Use it when a project needs a custom
|
|
45
|
+
board (no purchased module fits: **build-a-project** decision points), when
|
|
46
|
+
the board module or its `circuit.config.ts` changes, or when `protoboard
|
|
47
|
+
check` reports `CIRCUIT_*` findings. In **build-a-project** it is stage 5:
|
|
48
|
+
after **system-compose** and the parts, before **enclosure-cad**, which fits
|
|
49
|
+
the case around `out/board.glb` and aligns openings to the frames in
|
|
50
|
+
`out/board.manifest.json`.
|
|
51
|
+
|
|
52
|
+
## Inputs
|
|
53
|
+
|
|
54
|
+
| Input | Where | From |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| Board module | `src/boards/<module>/<module>.uhd.ts` | this SOP, step 1 (with **system-compose**) |
|
|
57
|
+
| Parts with package facts and pin designators | `parts/catalog/`, `parts/local/` | `protoboard parts add`, **uhd-part-author** |
|
|
58
|
+
| Size limit | a `design_envelope` trait on the board, from the brief | **system-compose** |
|
|
59
|
+
| Positions the enclosure fixes (connectors, buttons, displays, sensor windows) | the product's layout decisions | the brief, **enclosure-cad** |
|
|
60
|
+
| Provider config | `src/boards/<module>/circuit.config.ts` | step 2 |
|
|
61
|
+
| The provider | `npx protoboard doctor` shows `tscircuit-node` ✓ | `@protoboardai/ext-tscircuit` listed in `extensions` (the Protoboard installed on the machine carries it and every tscircuit package it pins; nothing to install with `tools install`) |
|
|
62
|
+
|
|
63
|
+
## Steps
|
|
64
|
+
|
|
65
|
+
### 0. Where a fix belongs: say it before you edit
|
|
66
|
+
|
|
67
|
+
Protoboard is the orchestrator. The board module and its parts own the
|
|
68
|
+
data, and the build steps (`circuit:generate`, `circuit:kicad`,
|
|
69
|
+
`circuit:lock`, `circuit:export`) turn UHD into tscircuit's and KiCad's
|
|
70
|
+
formats. Work in Protoboard: do not manage symbols, footprints, libraries or
|
|
71
|
+
models at the KiCad or tscircuit level, unless the job needs something
|
|
72
|
+
Protoboard cannot do (a SPICE model, for example). Then say so first, as the
|
|
73
|
+
fallback rule asks (Decision points).
|
|
74
|
+
|
|
75
|
+
Before any edit to a board, say in one line where the fix belongs, and make
|
|
76
|
+
it at the first of these levels that can hold it:
|
|
77
|
+
|
|
78
|
+
1. **The UHD model**: parts, connections, values, designators, MPN and LCSC
|
|
79
|
+
fields, test pads, the board's edge (step 1).
|
|
80
|
+
2. **The tscircuit stage**: placement (`placements` in `circuit.config.ts`,
|
|
81
|
+
`tscircuit/layout.json`), trace widths and rules, keep-outs, land patterns
|
|
82
|
+
(`tscircuit/footprints.json`), hand polish in `board.tsx` (steps 2 to 3).
|
|
83
|
+
3. **The stack itself**: a converter or provider bug (a garbled schematic,
|
|
84
|
+
mangled pin names, wrong silkscreen text) is reported as Protoboard
|
|
85
|
+
feedback, with the command and its output. Do not hand-patch its output.
|
|
86
|
+
4. **KiCad, last**, and only on a locked board: the hand finish the stages
|
|
87
|
+
above cannot produce (routing touch-ups, final placement nudges,
|
|
88
|
+
silkscreen; **kicad-layout-review**).
|
|
89
|
+
|
|
90
|
+
**A locked board is not fixed in place.** When the change belongs upstream
|
|
91
|
+
(levels 1 to 3), propose an unlock and a re-lock instead of editing KiCad.
|
|
92
|
+
Say plainly which KiCad work would be lost: `circuit:unlock` moves `kicad/`
|
|
93
|
+
to `kicad-archive/<stamp>/`, and nothing in it comes back by itself (the hand
|
|
94
|
+
routing, moved parts and silkscreen done since `lockedAt`). Unlocking is the
|
|
95
|
+
person's decision: ask. A small model change can instead be brought into
|
|
96
|
+
KiCad by hand (**kicad-layout-review** step 6).
|
|
97
|
+
|
|
98
|
+
**Separate design choices from mistakes.** When you list what to fix, mark
|
|
99
|
+
each item: a mistake (a wrong net, a value against the datasheet, a check
|
|
100
|
+
finding) or a design choice (where a part sits, a layer, a trade-off someone
|
|
101
|
+
made on purpose). Fix mistakes at the level they belong. Propose design
|
|
102
|
+
choices and ask before you change them.
|
|
103
|
+
|
|
104
|
+
**Edits that must survive a re-lock.** A KiCad edit the stack cannot make
|
|
105
|
+
yet (a logo, a zone the provider does not draw) is written down as a
|
|
106
|
+
post-lock step in `docs/post-lock.md`: what, why, the exact edit, how to
|
|
107
|
+
verify it. It is applied from that list after every lock. Never leave it as a
|
|
108
|
+
silent hand edit. When the stack can do it upstream, move it there.
|
|
109
|
+
|
|
110
|
+
#### How to work on a board
|
|
111
|
+
|
|
112
|
+
- **Say what a long process will do before you start it**: hand-routing, a
|
|
113
|
+
regroup of many parts, a sweep of several builds, a large part search. Say
|
|
114
|
+
what it does, roughly how long it takes (one build of this board takes
|
|
115
|
+
about as long as the last `circuit:generate`), and what it changes. When
|
|
116
|
+
the step is optional, ask first.
|
|
117
|
+
- **When the router fails, step back before routing by hand.** Find the
|
|
118
|
+
cause first. Parts may be too close (courtyards touching, no room for
|
|
119
|
+
vias), the rules may not fit (widths, clearances, via sizes for the pin
|
|
120
|
+
pitch), or the placement may force crossings (a connector on the wrong
|
|
121
|
+
side, a part turned against its nets). Propose that fix first: space or
|
|
122
|
+
move parts in `layout.json`, change a rule, give more room or layers.
|
|
123
|
+
- **Do not over-optimise small areas.** Before you polish a corner (a few
|
|
124
|
+
tenths of a millimetre on a trace, a perfect fan-out), ask whether it
|
|
125
|
+
needs doing at all for this board.
|
|
126
|
+
- **No placeholder CAD.** Never make a placeholder land pattern, CAD or 3D
|
|
127
|
+
model from a datasheet while the part's UHD artifacts or its JLC/LCSC
|
|
128
|
+
source has one. When no source has one, ask the person first.
|
|
129
|
+
|
|
130
|
+
### 1. Model the board
|
|
131
|
+
|
|
132
|
+
A board is an ordinary UHD module (UHD `docs/boards-and-nets.md`):
|
|
133
|
+
|
|
134
|
+
- **components** are its children, each a part with a `package` fact as the
|
|
135
|
+
manufacturer states it ("QFN-56 7×7 mm") and a pin designator (`pin`) on
|
|
136
|
+
every leaf interface. Decoupling capacitors, pull-ups, the regulator's
|
|
137
|
+
inductor and feedback divider are components too: the provider adds none;
|
|
138
|
+
- **generic passives** are the catalog's series parts (`yageo-rc0402`,
|
|
139
|
+
`yageo-rc0603`, `murata-grm155`, `murata-grm188`, `murata-blm15px`), one
|
|
140
|
+
part for every value: place each with its value in the child's
|
|
141
|
+
`overrides` (`{ id: "r3", moduleDefId: "yageo-rc0402", overrides: {
|
|
142
|
+
resistance: 4700 } }`; a capacitor sets `capacitance` and, for Murata GRM,
|
|
143
|
+
names the exact `part_number`). The part's `usage_note` (topic
|
|
144
|
+
`instance`) and its `overridable` list say which keys it takes. The
|
|
145
|
+
netlist, BOM, pick-and-place and the release use the placement's value and
|
|
146
|
+
MPN; `UHD_PASSIVE_OVERRIDE` reports a key the part does not take, a value
|
|
147
|
+
outside a tolerance grade's range, or an MPN that disagrees;
|
|
148
|
+
- **nets** are `Net(...)` interfaces of the board, joined to leaf pins by
|
|
149
|
+
links (`netLinks("sda", ["u1:pin_2", "u2:pin_14", "r3:pin_2"])`);
|
|
150
|
+
- the **edge** is its `exports` (the connector the cable mates, a debug port);
|
|
151
|
+
- a `design_envelope` trait carries the size limit with its reason;
|
|
152
|
+
- sub-modules group parts so the diagram stays readable (a power stage,
|
|
153
|
+
one LED channel used twice): a child that is `kind: "group"` or has children,
|
|
154
|
+
and states no package or part number, is expanded onto this PCB, its parts
|
|
155
|
+
under their paths (`led2/r1`). A sub-module's nets join the board's through a
|
|
156
|
+
pin it exports (an export cannot reuse its own net id: `gnd_port`); a bus
|
|
157
|
+
crosses whole, as one link between the two ports (`{ a: u1:i2c0, b:
|
|
158
|
+
sensor:i2c }`), because a pin bound into a port cannot be exported alone. A
|
|
159
|
+
child board (its own `circuit.config.ts` or `design_envelope`) or a
|
|
160
|
+
daughterboard mated by connector is never flattened (docs/specs/nested-boards.md).
|
|
161
|
+
|
|
162
|
+
**Regrouping a board that already exists** moves parts to new instance paths
|
|
163
|
+
(`u3` becomes `power/u3`). Say before you start what it changes and roughly
|
|
164
|
+
how long it takes (step 0). Then:
|
|
165
|
+
|
|
166
|
+
- designators stay: every `circuit:generate` records them per instance in
|
|
167
|
+
`tscircuit/refdes.json` (commit it), and a part that moved keeps its
|
|
168
|
+
designator when its id is unambiguous. Generate once before regrouping if
|
|
169
|
+
the board has no `refdes.json` yet;
|
|
170
|
+
- the hand-owned keys follow the paths: `layout.json`, `placements` in
|
|
171
|
+
`circuit.config.ts`, the instances in `footprints.json`, the nets in
|
|
172
|
+
`trace_widths`, selectors in `board.tsx`. Generate and check name a stale
|
|
173
|
+
key with the path it probably means;
|
|
174
|
+
- **the project's own scripts** that write those files (a placement or
|
|
175
|
+
net-width generator under `scripts/`) still write the old flat keys:
|
|
176
|
+
update them to the paths, or retire them with a note, before you run them
|
|
177
|
+
again. Re-running one undoes the regroup. The stack does not know about
|
|
178
|
+
them;
|
|
179
|
+
- outputs keyed by instance are stale until the board is generated and
|
|
180
|
+
exported again (`CIRCUIT_STALE`).
|
|
181
|
+
|
|
182
|
+
Every value comes from the parts' datasheets (cite them in the header:
|
|
183
|
+
the regulator's application circuit, the MCU's decoupling table). Never put a
|
|
184
|
+
footprint name, a layer or a tool name into the module.
|
|
185
|
+
|
|
186
|
+
The board is a child of the system (or a sub-assembly) like any part, so it
|
|
187
|
+
has an instance path and its exports link to cables (**harness-author**).
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
npx protoboard check --format json # UHD_NET, UHD_BUS_PULLUP, UHD_SUPPLY_BUDGET on the board's nets, UHD_PASSIVE_OVERRIDE on placements; no CIRCUIT_BOARD_UNREADABLE
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 2. Configure the provider
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
// src/boards/<module>/circuit.config.ts
|
|
197
|
+
import { defineCapabilityConfig } from "@protoboardai/core";
|
|
198
|
+
|
|
199
|
+
export default defineCapabilityConfig({
|
|
200
|
+
provider: "@protoboardai/ext-tscircuit",
|
|
201
|
+
constraints: {
|
|
202
|
+
layers: 2,
|
|
203
|
+
thickness_mm: 1.0,
|
|
204
|
+
outline: { circle: { diameter_mm: 30 } },
|
|
205
|
+
mounting_holes: [{ id: "mount_left", at: [-11, -6], diameter_mm: 2.2 }],
|
|
206
|
+
placements: { j1: { at: [0, 11.5], rotation_deg: 0 } }, // fixed by the enclosure
|
|
207
|
+
keepouts: [{ name: "antenna", rect: { at: [0, -12], size: [8, 3] }, reason: "chip antenna clearance (datasheet §…)" }],
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
| Constraint | Meaning |
|
|
213
|
+
| --- | --- |
|
|
214
|
+
| `layers` | 2 or 4 (default 2) |
|
|
215
|
+
| `thickness_mm` | board thickness (default 1.6) |
|
|
216
|
+
| `outline` | `{ circle: { diameter_mm } }`, `{ rect: { width_mm, height_mm } }` or `{ polygon: [[x, y], …] }` (mm, board frame); default: the board's `design_envelope` |
|
|
217
|
+
| `mounting_holes` | `[{ id, at: [x, y], diameter_mm }]` |
|
|
218
|
+
| `placements` | `{ <instance>: { at: [x, y], rotation_deg, side } }`: positions the enclosure fixes |
|
|
219
|
+
| `keepouts` | `[{ name, rect: { at, size } \| circle: { at, diameter_mm }, layers?, reason?, allow_bodies? }]`; on 4 layers `layers` may name `inner1`/`inner2`. A pour stays out of every keep-out on every layer. `allow_bodies: ["u1"]` lets that part's body (courtyard) lie over it, for the module whose own antenna the keep-out clears; its copper must still stay out. A keep-out the board's `design_envelope` states takes the same list from an entry with its name and no shape: `{ name: "antenna", allow_bodies: ["u1"] }` |
|
|
220
|
+
| `via_in_pad` | `avoid` (default: no via inside an SMD pad; check reports any as `CIRCUIT_VIA_IN_PAD`) or `fill_and_cap` (allowed; the order asks the fab to fill and cap them, IPC-4761 type VII) |
|
|
221
|
+
| `pours` | `[{ net, layers: (top\|bottom\|inner1\|inner2)[], clearance_mm?, reason? }]`: a copper pour (ground plane), filled after routing around the other nets, written to the Gerbers as regions. A keep-out must reach the board edge (a pour outline cannot hold a hole) |
|
|
222
|
+
| `rules` | `{ trace_width_mm, power_trace_width_mm, via_drill_mm, via_pad_mm, edge_clearance_mm, hole_clearance_mm? }`; `hole_clearance_mm` is checked: copper nearer a non-plated hole's wall is `CIRCUIT_HOLE_CLEARANCE` (round holes as circles) |
|
|
223
|
+
| `autorouter` | `auto_local` (default) or `sequential_trace`; cloud routing is refused |
|
|
224
|
+
| `routing` | `on` (default) or `off`: placement only, no autorouting; unrouted nets are then info |
|
|
225
|
+
| `trace_widths` | `{ nets?: { <net id>: mm }, classes?: { signal?, power?, ground?: mm } }`: width per net, else per class, else the rules'; a net routed narrower is `CIRCUIT_TRACE_WIDTH` |
|
|
226
|
+
| `via_touching_pad`, `via_under_body` | `allow` (default) or `avoid`: no via ring touching an SMD pad / no via inside a courtyard. The router has no such rule; check reports each (`CIRCUIT_VIA_TOUCHING_PAD`, `CIRCUIT_VIA_UNDER_BODY`) |
|
|
227
|
+
|
|
228
|
+
Board frame: mm, +X right and +Y up in the top view, +Z out of the top
|
|
229
|
+
copper, z = 0 on the board's mid-plane.
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
npx protoboard doctor # tscircuit-node ✓
|
|
233
|
+
npx protoboard capabilities # circuit: the board ✓ bound to @protoboardai/ext-tscircuit
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 2b. Land patterns, verified against the makers' drawings
|
|
237
|
+
|
|
238
|
+
Every part's land pattern comes from the provider's table
|
|
239
|
+
(`lib/land-patterns.ts`, matched by package, pin count, pitch, size and
|
|
240
|
+
maker) or from `tscircuit/footprints.json`. A land pattern is fab-ready only
|
|
241
|
+
when it is **verified**: its entry carries a `verification` record (document,
|
|
242
|
+
page, figure, date, and the drawing's values: pad groups with size, pitch and
|
|
243
|
+
row spacing; every pad centre where the drawing gives them; plated slots
|
|
244
|
+
and round holes with the centres the drawing puts them at; courtyard,
|
|
245
|
+
stencil, mask) and the copper matches it.
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
npx protoboard run circuit:check --module <board> --format json # no CIRCUIT_FOOTPRINT_UNVERIFIED
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
For a part with no entry, or an unverified one: open the maker's recommended
|
|
252
|
+
land pattern (the datasheet's "recommended land pattern / footprint /
|
|
253
|
+
soldering pattern", a separate land-pattern document such as Maxim's 90-xxxx,
|
|
254
|
+
or the connector's "recommended PCB layout"), author the pads in the part's
|
|
255
|
+
mounting frame (the frame its 3D body uses, so copper and body agree), and
|
|
256
|
+
write the record from the drawing, not from the pads. Include what the
|
|
257
|
+
drawing shows beyond pads: plated stake slots and peg holes (a USB-C shell,
|
|
258
|
+
a switch's guide bosses), paired contacts sharing one land (`merged`), the
|
|
259
|
+
thermal pad's stencil windows. Where the drawing gives no courtyard, mask or
|
|
260
|
+
stencil, say what you used (IPC-7351B nominal courtyard, 0.05 mm NSMD mask,
|
|
261
|
+
1:1 paste). A land pattern that only matches the part's CAD or package
|
|
262
|
+
outline is not verified. Board copper designed in the project (wire and test
|
|
263
|
+
pads) gets a record citing the pad part that states it.
|
|
264
|
+
|
|
265
|
+
### 3. Generate
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
npx protoboard run circuit:generate --module <board> --format json
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Look at `status`, `summary` and `data`: components and how each was placed
|
|
272
|
+
(fixed, layout, packed), nets, traces and vias, build time, `notes`. One
|
|
273
|
+
build of a 21-part board takes about 35 s and 1.2 GB of memory: run one at a
|
|
274
|
+
time.
|
|
275
|
+
|
|
276
|
+
Every build also writes the stencil and silkscreen of the authored land
|
|
277
|
+
patterns (tscircuit's own are 70 % paste per side, none on pill pads, and no
|
|
278
|
+
silkscreen). The silkscreen prints each part's designator (R12, U3) from
|
|
279
|
+
`tscircuit/refdes.json`, which generate extends for new parts and the KiCad
|
|
280
|
+
handoff keeps; the summary's log names any reference with no room on the
|
|
281
|
+
silkscreen. Generate also writes each part's body into the source as a `cadModel`
|
|
282
|
+
(its STEP, else GLB or OBJ, by a path relative to `tscircuit/`) and its
|
|
283
|
+
supplier part numbers (LCSC first, from `parts/offers.json` and
|
|
284
|
+
`parts/evidence/<part>/sourcing.json`), which the BOM and pick-and-place
|
|
285
|
+
carry too. A package matches a land pattern by name or by family
|
|
286
|
+
(`SOT23-5L` is a `SOT-23-5`), its pin count, pitch and size agreeing;
|
|
287
|
+
when entries of its family do not fit, generate stops and check says why
|
|
288
|
+
(`CIRCUIT_FOOTPRINT_MISMATCH`). Generate writes `tscircuit/board.gen.tsx` (rewritten every time), and once
|
|
289
|
+
each `tscircuit/board.tsx` (your entry; wrap or replace the generated board
|
|
290
|
+
there) and `tscircuit/layout.json` (positions for parts the config does not
|
|
291
|
+
fix). Parts in neither are placed by tscircuit's packer, which fails on
|
|
292
|
+
dense boards ("Unable to pack all PCB components"); placement errors stop
|
|
293
|
+
routing and the run fails saying so. Add positions to `layout.json`
|
|
294
|
+
(`{ "<instance>": { "at": [x, y], "rotation_deg": 0 } }`) and generate
|
|
295
|
+
again. Land-pattern overrides for a package or one instance go in
|
|
296
|
+
`tscircuit/footprints.json`; a part with no package fact, or a package with
|
|
297
|
+
no entry in the provider's table, stops generate with a message saying which.
|
|
298
|
+
|
|
299
|
+
### 4. Export
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
npx protoboard run circuit:export --module <board>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
`out/` is replaced (rebuilding first if the native source changed; the
|
|
306
|
+
export is staged, so a failed export leaves the last good `out/` in place): `gerber/*.gbr` and `gerber/*.drl`, `bom.csv` and `pnp.csv` keyed by
|
|
307
|
+
UHD instance path, `board.glb`, `board.step`, `board.manifest.json` (outline,
|
|
308
|
+
`board_top`, mounting holes, every component and export with its frame),
|
|
309
|
+
`netlist.json`, and `schematic.svg`, `pcb-top.svg`, `pcb-bottom.svg` for the
|
|
310
|
+
viewer. Read the summary for which body each component got (its part's STEP,
|
|
311
|
+
a box from package facts, or tscircuit's), and for any body `board.glb`
|
|
312
|
+
could not load (it names them; the converter drew a box instead).
|
|
313
|
+
|
|
314
|
+
### 5. Extract the frames
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
npx protoboard run circuit:extract --module <board> --format json
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`data.frames` holds a frame for each of the board's exports and interfaces
|
|
321
|
+
(a connector's mouth with the mating direction as its normal; a component's
|
|
322
|
+
top; a mechanical interface on the underside), plus the board's artifacts
|
|
323
|
+
and the placements. It does not edit the module: copy the frames into the
|
|
324
|
+
board module's geometry by hand (there is no re-bind operation for boards
|
|
325
|
+
yet), then `protoboard check`. The artifacts need no copying: the provider
|
|
326
|
+
declares the board's outputs (`out/`, the routed `tscircuit/circuit.json`,
|
|
327
|
+
and a locked board's KiCad project), and Protoboard attaches each one on
|
|
328
|
+
disk to the board when the project loads. That is what gives the board its
|
|
329
|
+
Schematic, PCB, 3D and BOM tabs in the viewer. An artifact the board lists
|
|
330
|
+
itself with the same id or file overrides the provider's. **enclosure-cad** reads the same frames from
|
|
331
|
+
`out/board.manifest.json`.
|
|
332
|
+
|
|
333
|
+
### 6. Check
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
npx protoboard run circuit:check --module <board> --format json # this board only
|
|
337
|
+
npx protoboard check --format json # all of it: circuit:check per board, standard-outputs, UHD rules
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Look for `ext:@protoboardai/ext-tscircuit/circuit:check` in `checks[]` with
|
|
341
|
+
the board as `subject`, and `protoboard/standard-outputs` for its `out/`.
|
|
342
|
+
Hand polish (`<tracehint>`, `<pcbtrace>` in `board.tsx`) is allowed; check
|
|
343
|
+
then compares the result with the model like any other change.
|
|
344
|
+
|
|
345
|
+
### 7. View the board (the definition of done)
|
|
346
|
+
|
|
347
|
+
The board is done when the person can see it in the viewer, not when the
|
|
348
|
+
commands pass. Open the project in VS Code: the Protoboard viewer shows the
|
|
349
|
+
board's Diagram, 3D (`out/board.glb` with the parts' own bodies), Schematic,
|
|
350
|
+
PCB (top and bottom) and BOM tabs (`npx protoboard dev` serves the same
|
|
351
|
+
viewer in a browser). This is the viewer for a stack board; `tsci dev` is not
|
|
352
|
+
(it re-routes the board with another core).
|
|
353
|
+
|
|
354
|
+
Then check every view headless:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
npx protoboard views check --module <board> # each tab opened once in a headless browser; exit 1 on an empty or erroring tab
|
|
358
|
+
npx protoboard views check --module <board> --out-dir <folder> # also a PNG of every tab, to look at
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
It lists the tabs the board gets and fails on a tab that throws, shows an
|
|
362
|
+
empty state, or draws nothing. A board with no Schematic or PCB tab although
|
|
363
|
+
`out/` holds the drawings is reported by `protoboard check` as
|
|
364
|
+
`PB_VIEW_UNREACHABLE`, with the reason. Look at the PNGs before you say the
|
|
365
|
+
board is done: a tab that draws can still draw the wrong board.
|
|
366
|
+
|
|
367
|
+
### 8. Hand the board to KiCad (lock), when a person says so
|
|
368
|
+
|
|
369
|
+
KiCad is the final hand-placement step: tscircuit is the layout of record
|
|
370
|
+
while the design changes, KiCad after the board is locked (the spec:
|
|
371
|
+
`docs/specs/pcb-kicad-handoff.md` in the stack repository). **Locking is a human
|
|
372
|
+
decision: ask the person before `circuit:lock` or `circuit:unlock`, and never
|
|
373
|
+
lock to get past a failing check.**
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
npx protoboard run circuit:kicad --module <board> # a KiCad 10 preview in tscircuit/kicad/ (any time; nothing changes hands; not committed)
|
|
377
|
+
npx protoboard run circuit:lock --module <board> # asked first: kicad/ (committed) + kicad/handoff.json; KiCad is the layout of record
|
|
378
|
+
npx protoboard run circuit:unlock --module <board> # asked first: kicad/ moves to kicad-archive/<stamp>/; back to tscircuit
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
The preview in `tscircuit/kicad/` is rewritten each time and is not
|
|
382
|
+
committed (the project's `.gitignore` leaves it out); the locked project in
|
|
383
|
+
`kicad/` is committed. `circuit:lock` needs KiCad 10 (`kicad-cli`) and a
|
|
384
|
+
build that follows the model (generate first). Reference designators are kept per instance in
|
|
385
|
+
`tscircuit/refdes.json`, so a re-lock keeps them. After lock:
|
|
386
|
+
|
|
387
|
+
- `circuit:generate` refuses (it would replace the routing of record);
|
|
388
|
+
`--force` rebuilds tscircuit's copy without touching KiCad.
|
|
389
|
+
- `circuit:export` makes `out/` from KiCad with `kicad-cli` and stops on a
|
|
390
|
+
KiCad DRC error (`--force` exports anyway; `out/drc.json` keeps the report).
|
|
391
|
+
- `circuit:check` compares the model, the handoff record and the KiCad files
|
|
392
|
+
(`CIRCUIT_LOCKED`, `CIRCUIT_KICAD_*`); the review in KiCad is the
|
|
393
|
+
**kicad-layout-review** skill.
|
|
394
|
+
|
|
395
|
+
## Decision points
|
|
396
|
+
|
|
397
|
+
| Decision | Rule |
|
|
398
|
+
| --- | --- |
|
|
399
|
+
| 2 or 4 layers | 2 unless routing fails with every part placed, or a part's datasheet asks for a solid ground plane (a radio, a fine-pitch BGA, a high-speed bus). |
|
|
400
|
+
| Fixed placement | A connector, button, display or sensor window the enclosure serves is fixed in `placements`; everything else may move (`layout.json` or the packer). |
|
|
401
|
+
| Outline | From the `design_envelope` unless the enclosure needs another shape; say why in the config comment. |
|
|
402
|
+
| Generated or hand-polished routing | Generated until the board is final; polish only in `board.tsx`, never in `board.gen.tsx` or `out/`. |
|
|
403
|
+
| A part with no land-pattern entry | Add an entry from the manufacturer's recommended land pattern with its verification record (step 2b): to the provider's table when the maker's pattern serves every project, else to `tscircuit/footprints.json`; never copy a library you do not hold the rights to. |
|
|
404
|
+
| Vias in pads | `avoid` (the default). Move parts (`layout.json`) until the router keeps them out; `fill_and_cap` only when a part's pitch leaves no room, and then the order carries the fill note. |
|
|
405
|
+
| The router leaves errors | Step back first (step 0, "How to work on a board"): parts too close, rules that do not fit, a placement that forces crossings. Fix that and propose it. Then, for a last few errors, the result depends on small position changes: try a few 0.1-0.3 mm moves of the part named in the finding, in parallel builds, and keep the clean one. Say how many builds and how long before you start. |
|
|
406
|
+
| KiCad files wanted | Fabrication needs only `out/gerber/`. A KiCad 10 project of the current build at any time: `circuit:kicad` (step 8; a preview, not committed). KiCad as the layout of record for the hand finish: `circuit:lock`, when the person decides (step 8), then **kicad-layout-review**. |
|
|
407
|
+
| A fix for the board | Say where it belongs before editing (step 0): the model, then the tscircuit stage, then a stack bug to report, then KiCad. On a locked board, propose unlock and re-lock for an upstream change, and say what KiCad work it would lose. |
|
|
408
|
+
| The official path fails and a board is needed anyway | Fix what can be fixed inside it first (land pattern, `layout.json`, keep-out frame). With a real blocker and a use case that needs the board regardless, you may fall back to `tsci` or KiCad tooling (`kicad-cli`, KiCad skills such as KiStack's), but **first** tell the user plainly that you are leaving the official path, why, and that it adds friction (the outputs leave the stack's checks and staleness tracking; changes must be carried back to the UHD model by hand); record it in the project's `worklog.md` and as Protoboard feedback; keep the UHD model the source of truth; and write the outputs only to `src/boards/<module>/fallback-<tool>/` with a README. Full policy: [references/pcb-workflow.md](references/pcb-workflow.md). |
|
|
409
|
+
| Ready for fabrication | 0 `CIRCUIT_*` findings (every land pattern verified, no via in a pad), and you have looked at the Gerber layers (copper, mask, paste, silkscreen, drill) and the 3D board. Then a second DRC with the fab's own rules: the provider's rules are looser than a fab's. Nothing is uploaded or ordered. |
|
|
410
|
+
|
|
411
|
+
## Outputs
|
|
412
|
+
|
|
413
|
+
| Output | Where |
|
|
414
|
+
| --- | --- |
|
|
415
|
+
| Board module and config | `src/boards/<module>/<module>.uhd.ts`, `circuit.config.ts` |
|
|
416
|
+
| Native design | `src/boards/<module>/tscircuit/`: `board.gen.tsx`, `circuit.json`, `build.json`, `models/` (generated); `board.tsx`, `layout.json`, `footprints.json` (yours) |
|
|
417
|
+
| Standard outputs (generated; never edit) | `src/boards/<module>/out/`: `gerber/`, `bom.csv`, `pnp.csv`, `board.glb`, `board.step`, `board.manifest.json`, `netlist.json`, SVG renders |
|
|
418
|
+
| Operation records | `operations/` |
|
|
419
|
+
|
|
420
|
+
## Verification
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
npx protoboard check --format json # circuit:check ran for the board with 0 errors and no CIRCUIT_FOOTPRINT_UNVERIFIED; standard-outputs ran with no PB_OUTPUT_* finding
|
|
424
|
+
npx protoboard run circuit:export --module <board> # a re-export of an unchanged board changes no file (git status --short src/boards)
|
|
425
|
+
npx protoboard views check --module <board> # every tab of the board draws (Diagram, 3D, Schematic, PCB, BOM)
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
The board's `design_envelope`, fixed placements and keep-outs all hold
|
|
429
|
+
(no `UHD_DESIGN_ENVELOPE`, `CIRCUIT_PLACEMENT_MOVED`, `CIRCUIT_KEEPOUT`), and
|
|
430
|
+
**system-review** has looked at the board's nets.
|
|
431
|
+
|
|
432
|
+
## Failure handling
|
|
433
|
+
|
|
434
|
+
| Code or symptom | Means | Do |
|
|
435
|
+
| --- | --- | --- |
|
|
436
|
+
| `CIRCUIT_BOARD_UNREADABLE` | A part undefined, a pin without a designator, a composite linked to a pin or a net, two nets of one module joined, a link to a sub-module's own net or into a separate board, `quantity` on a sub-module | Fix the board module, or add designators to the part (**uhd-part-author**) |
|
|
437
|
+
| `CIRCUIT_BOARD_GROUPING` | A sub-module's export the board leaves unconnected, or a sub-module with no parts | Link the export, or mark it `unconnected` on the child with the reason |
|
|
438
|
+
| `CIRCUIT_NOT_BUILT` | No build yet | `run circuit:generate --module <board>` |
|
|
439
|
+
| `CIRCUIT_STALE` | The board, `layout.json`, `footprints.json` or the constraints changed since generate, or the native source (or a file it imports, JSON included) since the build | `run circuit:generate`, then `circuit:export` |
|
|
440
|
+
| `CIRCUIT_STALE` (`out/ was exported from other inputs`) | An input of the last export changed: the built board, what the board depends on in UHD (parts, pins, nets, packages, bodies, edge, envelope; not descriptions or prices), a body file, the provider's code or a converter (the message names it; `tscircuit/export.json` records them). The same inputs give the same `board.step` and `board.glb`, so this is the only way a re-export changes them | `run circuit:export`, then regenerate what is built around the board (enclosure) |
|
|
441
|
+
| `CIRCUIT_DRC` | tscircuit's DRC or `@tscircuit/checks` found an error | Give parts room (`layout.json`, a larger outline, 4 layers); regenerate |
|
|
442
|
+
| `CIRCUIT_NET_OPEN`, `CIRCUIT_NET_SHORT`, `CIRCUIT_NET_EXTRA`, `CIRCUIT_UNROUTED`, `CIRCUIT_PIN_MISSING` | The copper's nets differ from the UHD nets | Regenerate; if routing cannot complete, move parts or add layers. Never change the UHD nets to match the copper |
|
|
443
|
+
| `CIRCUIT_PAD_UNNETTED` | A pin with several pads (a module's exposed ground pads) has pads the copper does not join to its net (boards generated before 0.1.7 netted only the first) | `run circuit:generate` (it joins every SMD pad of the pin), then `circuit:export` |
|
|
444
|
+
| `CIRCUIT_FINGERPRINT_FORMAT` (info) | `tscircuit/export.json` is from a provider that fingerprinted the whole UHD module; the board's own dependencies are fingerprinted now, so a description or price edit no longer makes `out/` stale | `run circuit:export` once to record the new format |
|
|
445
|
+
| `tsci dev` shows another routing, sits at 95 %, or crashes with ENOENT | tsci rebuilds with its own core and watches files the provider rewrites | Stop it; view the board in the Protoboard viewer (step 7). Never run tsci on a stack board |
|
|
446
|
+
| `CIRCUIT_FOOTPRINT_MISMATCH` | Land patterns of the package's name or family exist, none fits its facts | Correct the part's package facts (**uhd-part-author**), or add a fitting entry to `tscircuit/footprints.json` |
|
|
447
|
+
| `CIRCUIT_FOOTPRINT_FAMILY` (info) | The land pattern was chosen by package family | Nothing; confirm it for an unusual part |
|
|
448
|
+
| `CIRCUIT_VIA_TOUCHING_PAD`, `CIRCUIT_VIA_UNDER_BODY` | A via against the config's via policy | Move parts (`layout.json`) a little and generate again |
|
|
449
|
+
| `CIRCUIT_HOLE_CLEARANCE` | Copper too near a non-plated hole | Move the part, the trace or the hole; a pour is cut by the router's clearance |
|
|
450
|
+
| `CIRCUIT_TRACE_WIDTH` | The router necked a net narrower than `trace_widths` asks | Give the net room, or accept it |
|
|
451
|
+
| `CIRCUIT_COMPONENT_MISSING`, `CIRCUIT_COMPONENT_EXTRA` | Board and model disagree on components | Regenerate; remove hand-added parts from `board.tsx` |
|
|
452
|
+
| `CIRCUIT_OUTSIDE_OUTLINE` | A pad off the board, or a body overhanging | Move the part or enlarge the outline |
|
|
453
|
+
| `CIRCUIT_KEEPOUT` | Copper in a keep-out, or a keep-out that misses the board | Move the part or trace; fix the keep-out |
|
|
454
|
+
| `CIRCUIT_PLACEMENT_MOVED` | A fixed part is elsewhere | Remove the override in `board.tsx` or `layout.json`; regenerate |
|
|
455
|
+
| `CIRCUIT_FOOTPRINT_UNVERIFIED` | A land pattern has no verification record, or differs from it | Step 2b; the message names the entry, the instances and what differs. The work order holds the board until none is left |
|
|
456
|
+
| `CIRCUIT_VIA_RULES` | A via is smaller than `rules.via_pad_mm` / `via_drill_mm` (written by hand) | Make it to the rules, or change the rules only if the fab can make it |
|
|
457
|
+
| `CIRCUIT_HAND_PAD_JOIN` | A trace written by hand in `board.tsx` joins pads of one pin, which generate joins itself (in KiCad it is copper on no net) | Remove it from `board.tsx`, then `run circuit:generate` |
|
|
458
|
+
| `CIRCUIT_VIA_IN_PAD` | The router put a via in an SMD pad | Move the part or its neighbours a little and generate again; or `via_in_pad: "fill_and_cap"` |
|
|
459
|
+
| `pcb_component_outside_board_error` for a part turned by an odd angle at the edge | @tscircuit/checks tests the part's axis-aligned box, not its pads | If every pad and the courtyard are inside, allow it off the board in `board.tsx` (`allowOffBoard`); `CIRCUIT_OUTSIDE_OUTLINE` still checks the pads |
|
|
460
|
+
| generate fails: "Unable to pack all PCB components" or "Not routed" | The packer or the router gave up | Add positions to `tscircuit/layout.json`; generate again |
|
|
461
|
+
| generate fails: no land pattern for a package | The provider's table has no entry | Add one to `tscircuit/footprints.json` (cited) |
|
|
462
|
+
| `PB_CAPABILITY_CONSTRAINT_UNKNOWN` | A constraint this provider does not take | Check the name against the table in step 2 |
|
|
463
|
+
| `PB_OUTPUT_UNKNOWN_INSTANCE`, `PB_OUTPUT_INCOMPLETE` | `out/` is from an older board | Re-export |
|
|
464
|
+
| `UHD_NET`, `UHD_BUS_PULLUP` | The board's nets are wrong or incomplete | Fix the board module (step 1) |
|
|
465
|
+
| generate or export keeps failing on a board that is otherwise done | tscircuit cannot finish it (placement, routing, an exporter fault) | Fallback: `run circuit:kicad` and inspect the preview with the **kicad-cli** skill; the model and tscircuit stay the truth. If KiCad should finish the board, ask the person whether to lock (step 8) |
|
|
466
|
+
| `CIRCUIT_LOCKED`, `CIRCUIT_KICAD_*` | The board is locked; KiCad is the layout of record | **kicad-layout-review** |
|
|
467
|
+
|
|
468
|
+
Every other code: **system-compose** [diagnostic codes](../system-compose/references/diagnostic-codes.md).
|
|
469
|
+
|
|
470
|
+
## Hard rules
|
|
471
|
+
|
|
472
|
+
1. The board module stays tool-neutral: no footprint names, layers or tool
|
|
473
|
+
names in UHD; land patterns live in the provider's table and
|
|
474
|
+
`footprints.json`.
|
|
475
|
+
2. Never edit `board.gen.tsx` or `out/`; change the module, the config,
|
|
476
|
+
`layout.json` or `board.tsx` and regenerate.
|
|
477
|
+
3. No cloud routing, no uploads, no orders: the provider runs offline and
|
|
478
|
+
stops at files.
|
|
479
|
+
4. No `tsci dev`, `tsci build` or `tsci export` on a stack board or at the
|
|
480
|
+
project root; the provider's operations are the way.
|
|
481
|
+
5. Leaving the official path (tsci, KiCad tooling) is a fallback for a real
|
|
482
|
+
blocker only: announced to the user first, recorded in the worklog and as
|
|
483
|
+
Protoboard feedback, outputs in `fallback-<tool>/`, the UHD model kept the
|
|
484
|
+
source of truth.
|
|
485
|
+
6. Locking and unlocking are the person's decisions: ask first. After lock,
|
|
486
|
+
parts and connections still change in the board module first; nothing is
|
|
487
|
+
written back from KiCad.
|
|
488
|
+
7. Say where a fix belongs before editing a board: the model, then the
|
|
489
|
+
tscircuit stage, then a stack bug (reported, not patched), then KiCad. A
|
|
490
|
+
locked board is not fixed in place: propose unlock and re-lock, and say
|
|
491
|
+
what would be lost. A KiCad edit that must survive a re-lock is a
|
|
492
|
+
post-lock step in `docs/post-lock.md`.
|
|
493
|
+
8. Never make placeholder CAD or 3D models from datasheets while the part's
|
|
494
|
+
UHD artifacts or its JLC/LCSC source has one; ask first when none does.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# The PCB workflow, KiCad files, and leaving the official path
|
|
2
|
+
|
|
3
|
+
Read with the **circuit-board** SOP.
|
|
4
|
+
|
|
5
|
+
## The sanctioned workflow
|
|
6
|
+
|
|
7
|
+
1. Model the board in UHD (`src/boards/<module>/<module>.uhd.ts`): parts, pins, nets, edge, envelope.
|
|
8
|
+
2. `protoboard run circuit:generate --module <board>`: the provider writes the tscircuit source and
|
|
9
|
+
routes it, in-process, with the tscircuit core it pins.
|
|
10
|
+
3. `protoboard run circuit:check --module <board>` (also part of `protoboard check`): DRC, the copper
|
|
11
|
+
against the UHD nets, every pad netted, outline, keep-outs, placements, staleness.
|
|
12
|
+
4. `protoboard run circuit:export --module <board>`: Gerber and drill, BOM, pick-and-place, GLB,
|
|
13
|
+
STEP, manifest, netlist and renders in `out/`. A failed export leaves the last good `out/`.
|
|
14
|
+
5. View it in VS Code: the Protoboard extension's viewer shows `out/pcb-top.svg`,
|
|
15
|
+
`out/pcb-bottom.svg`, `out/schematic.svg` and the 3D board `out/board.glb` (with the parts' own
|
|
16
|
+
bodies). `protoboard dev` serves the same viewer in a browser.
|
|
17
|
+
|
|
18
|
+
### Why not `tsci`
|
|
19
|
+
|
|
20
|
+
Agents never run `tsci dev`, `tsci build` or `tsci export` on a stack board, nor at a stack
|
|
21
|
+
project's root. What happened on a large field project:
|
|
22
|
+
|
|
23
|
+
- `tsci` rebuilds `board.tsx` with its own, different tscircuit core (0.0.2125 or later, against
|
|
24
|
+
the provider's pinned 0.0.2056) and re-routes the board in the browser, so what it shows is not
|
|
25
|
+
the board the provider built, checked and exported.
|
|
26
|
+
- Its file watcher crashes (ENOENT) when generate or export rewrites the files it watches.
|
|
27
|
+
- It serves files only under `/api/files/static/`, which led to model paths that work nowhere
|
|
28
|
+
else; `tsci build` left `dist/` and `.tscircuit/` in the project.
|
|
29
|
+
- Its outputs bypass `circuit:check` and the staleness records, so nothing says when they no
|
|
30
|
+
longer match the model.
|
|
31
|
+
|
|
32
|
+
`tsci` is fine for a standalone tscircuit component outside the project, or to reproduce a
|
|
33
|
+
tscircuit bug for an upstream report.
|
|
34
|
+
|
|
35
|
+
## UHD → tscircuit → KiCad
|
|
36
|
+
|
|
37
|
+
The chain: **tscircuit while the design changes** (agents iterate: model,
|
|
38
|
+
generate, route, check), then the design is **locked** and exported to **KiCad** for the final
|
|
39
|
+
hand placement and routing; after the lock KiCad is the board's source of truth for copper, and
|
|
40
|
+
the UHD model stays the source of truth for what is on the board and how it is connected (check
|
|
41
|
+
compares the two both ways). Not UHD → KiCad directly.
|
|
42
|
+
|
|
43
|
+
### KiCad files
|
|
44
|
+
|
|
45
|
+
The provider writes them (spec `docs/specs/pcb-kicad-handoff.md` in the stack
|
|
46
|
+
repository); never `tsci export` on a stack board:
|
|
47
|
+
|
|
48
|
+
- `protoboard run circuit:kicad --module <board>`: a KiCad 10 project of the current build in
|
|
49
|
+
`tscircuit/kicad/`, a preview at any time (footprint and symbol libraries of the board, the
|
|
50
|
+
parts' 3D bodies under `3d/` linked through `${KIPRJMOD}`, MPN, manufacturer, LCSC and the UHD
|
|
51
|
+
instance on every symbol and footprint, stable reference designators). Nothing changes hands.
|
|
52
|
+
The preview is rewritten each time and is not committed (the project's `.gitignore` leaves
|
|
53
|
+
`src/boards/*/tscircuit/kicad/` out).
|
|
54
|
+
- `protoboard run circuit:lock --module <board>`, **only when the person decides**: the project
|
|
55
|
+
into `<board dir>/kicad/` with `handoff.json`; KiCad is then the layout of record, `circuit:export`
|
|
56
|
+
makes `out/` with `kicad-cli`, and `circuit:check` compares the model, the handoff and the KiCad
|
|
57
|
+
files both ways (`CIRCUIT_KICAD_*`). `circuit:unlock` returns the board to tscircuit and keeps
|
|
58
|
+
the KiCad folder as an archive.
|
|
59
|
+
- **Fabrication does not need KiCad** before lock: `out/gerber/` is a complete fabrication set.
|
|
60
|
+
|
|
61
|
+
## Fallback policy
|
|
62
|
+
|
|
63
|
+
When the official path fails **and** the use case needs a board anyway (a PCB at all costs, with a
|
|
64
|
+
real blocker: the router cannot complete a 4-layer board, a converter crashes on this board, a
|
|
65
|
+
fab deadline before a provider fix), the agent may fall back to `tsci` or to KiCad tooling
|
|
66
|
+
(the `circuit:kicad` preview inspected with the **kicad-cli** skill, derived from KiStack, an
|
|
67
|
+
MIT-licensed KiCad skill pack).
|
|
68
|
+
Before it does:
|
|
69
|
+
|
|
70
|
+
1. **Tell the user plainly, before running anything**, that it is leaving the official path, why
|
|
71
|
+
(the failing command and its message), and that it adds friction: the fallback's outputs are
|
|
72
|
+
outside the stack's checks (`circuit:check`, the standard-output checks) and its staleness
|
|
73
|
+
tracking, and every change made there must be carried back to the UHD model by hand.
|
|
74
|
+
2. **Record it** in the project's worklog (`worklog.md`: date, the blocker, the fallback tool and
|
|
75
|
+
version, what was produced, what must be carried back) and as Protoboard feedback (the feedback
|
|
76
|
+
tool of a connected Protoboard MCP server or, in a company workspace, its friction log), so the
|
|
77
|
+
blocker reaches the stack's maintainers.
|
|
78
|
+
3. **Keep the UHD model the source of truth.** Never change the model to match the fallback's
|
|
79
|
+
copper; a net or part change goes into the model first, then into the fallback by hand.
|
|
80
|
+
4. **Put the fallback's outputs in a clearly named folder** next to the board, never in `out/`
|
|
81
|
+
or `tscircuit/`: `src/boards/<module>/fallback-<tool>/` (for example `fallback-kicad/`), with a
|
|
82
|
+
`README.md` naming the blocker, the date, the source (`tscircuit/circuit.json` of which build)
|
|
83
|
+
and the tool.
|
|
84
|
+
5. Go back to the official path when the blocker is fixed, and say so in the worklog.
|
|
85
|
+
|
|
86
|
+
A blocker the agent can fix inside the official path (a missing land pattern, a part that needs
|
|
87
|
+
room in `layout.json`, a keep-out in the wrong frame) is not a reason to fall back.
|