name: adom-tscircuit-skill description: Use when building Adom molecule projects with tscircuit β connector families, design-group generators, molecule sizing/placement, machine contacts, review servers, DRC scripts, batch snapshots. Triggers on "tscircuit molecule", "connector molecule", "molecule generator", "design group", "snapshot review", "tscircuit DRC", "molecule sizing", "machine contact placement".
tscircuit Project Workflow
Step-by-step guide for building tscircuit component projects on the Adom platform β from blank template to reviewed, buildable molecule families.
This skill covers the Adom-specific workflow: molecule sizing, machine contact placement, design-group generators, review/approval servers, and DRC tooling. For general tscircuit usage (CLI commands, JSX syntax, footprints, nets, traces), reference docs are bundled with this skill:
CLI.mdβ tsci CLI commands and flagsSYNTAX.mdβ tscircuit JSX syntax referenceWORKFLOW.mdβ general tscircuit workflow patternsCHECKLIST.mdβ pre-export validation checklistIMPORT-FOOTPRINT.mdβ importing components from JLCPCB, verifying footprints, fixing common issues (bad pin labels, wrong 3D offset)
READ THIS FIRST β Adom Molecule Essentials
Before writing ANY molecule code, understand these three things:
1. Machine contacts are NOT generic chips
External connections in Adom molecules use MachineContactMedium β a specific component with defined hole sizes, bounding boxes, and placement rules. Never use <chip> with footprint="0402" as a stand-in for contacts.
// WRONG β generic chip as fake contact
<chip name="MC1" footprint="0402" pinLabels={{ pin1: "pos", pin2: "neg" }} pcbX={-10} pcbY={5} />
// RIGHT β Adom machine contact
import { MachineContactMedium } from "@tsci/adom-inc.library"
<MachineContactMedium name="MC1" pcbX={-8} pcbY={2} />
2. Required imports for every molecule
import { Molecule, ValidateContacts } from "@tsci/adom-inc.molecule"
import { MachineContactMedium } from "@tsci/adom-inc.library"
3. Minimal molecule structure
export default () => (
<Molecule type="4pin" size="16x8" pinType="MachinePinMediumShort" wing="nominal" roundEdges={true}>
<MyChip name="U1" />
{/* Machine contacts in the margins β NOT generic chips */}
<MachineContactMedium name="MC1" pcbX={-8} pcbY={2} />
<MachineContactMedium name="MC2" pcbX={-8} pcbY={0} />
<ValidateContacts />
{/* Traces connect contacts to IC pins */}
<trace from="MC1.pin1" to="U1.VCC" />
<trace from="MC2.pin1" to="U1.TXD" />
{/* Power/ground to corner machine pins */}
<trace from="MP1.pin1" to="net.GND" />
<trace from="MP2.pin1" to="net.VCC3V3" />
<copperpour layer="bottom" connectsTo="net.GND" />
</Molecule>
)
Read the full skill below for details on sizing, placement, datasheet research, and DRC. But never skip these three fundamentals.
FIRST: Environment Setup Check
IMPORTANT β Run this before ANY tscircuit work. If any check fails, fix it before proceeding.
# 1. Bun installed?
~/.bun/bin/bun --version
# Expected: 1.x.x
# If NOT found, install it:
# curl -fsSL https://bun.sh/install | bash
# export PATH="$HOME/.bun/bin:$PATH"
# If found but not on PATH:
# export PATH="$HOME/.bun/bin:$PATH"
# 2. tsci CLI available?
~/.bun/bin/bunx tsci --version
# Expected: 0.x.x β if fails, bun is not installed (fix step 1)
# 3. Can fetch Adom packages? (public dist β no login needed)
mkdir -p /tmp/tsci-test && cd /tmp/tsci-test
echo '@tsci:registry=https://npm.tscircuit.com' > .npmrc
~/.bun/bin/bun add @tsci/adom-inc.molecule @tsci/adom-inc.library
# Expected: packages install successfully
# If 401/403 error: run `bunx tsci login` (browser auth, adom-inc org member) and retry
rm -rf /tmp/tsci-test
If all 3 checks pass, the environment is ready. Proceed to "Creating a New Project" or the step relevant to the user's task.
Note: Bun may NOT be pre-installed on new containers. If ~/.bun/bin/bun doesn't exist, install with curl -fsSL https://bun.sh/install | bash. Adom packages are distributed via tscircuit's public dist β no login is required to install them. Only run bunx tsci login if bun add fails with a 401/403 error, or if you need to publish packages (tsci push).
Prerequisites & Setup
Runtime: Bun
Bun is the required JavaScript runtime for all tscircuit projects. It may or may not be pre-installed β check ~/.bun/bin/bun --version and install with curl -fsSL https://bun.sh/install | bash if missing.
If bun is not on PATH, add it permanently:
export PATH="$HOME/.bun/bin:$PATH"
echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.bashrc
tscircuit CLI
Always use bunx tsci (project-local CLI from node_modules) instead of the globally installed tsci. The project-local version is newer and has bug fixes not in the global install.
# Correct β uses project-local CLI
bunx tsci build
bunx tsci snapshot --pcb-only --update
# Avoid β global CLI may have known bugs
tsci build
tscircuit Registry
Adom packages (@tsci/adom-inc.library, @tsci/adom-inc.molecule) are available via tscircuit's public dist β no authentication is required to install them. The .npmrc file in each project points the @tsci scope to the tscircuit registry:
@tsci:registry=https://npm.tscircuit.com
If bun install fails with a 401/403 error (unlikely with public dist), authenticate:
bunx tsci login
# Opens browser for tscircuit.com authentication β must be an adom-inc org member
Authentication is also required for publishing packages (tsci push).
Creating a New Project
IMPORTANT: tsci init -y does NOT exist. Create projects manually. Full templates (package.json, tsconfig.json, tscircuit.config.json, package.json scripts, Molecule Required Props, Build Output Path Mapping): see project-setup-reference.md.
mkdir -p ~/project/user-content/MyChip-Molecule/lib
cd ~/project/user-content/MyChip-Molecule
# Create package.json, tsconfig.json, tscircuit.config.json (see project-setup-reference.md)
~/.bun/bin/bun install
Cloning an existing project (e.g., JST connectors):
git clone https://github.com/adom-inc/adom-tsci-JST-connectors.git ~/project/adom-tsci-JST-connectors
cd ~/project/adom-tsci-JST-connectors
~/.bun/bin/bun install
Molecule Required Props (all required β missing any causes a runtime crash):
type ("4pin"/"2pin"), size ("16x8" format), pinType ("MachinePinMediumStandard"/"MachinePinMediumShort"), wing="nominal". Full prop table and crash messages: see project-setup-reference.md.
Toolkit Contents
The skill includes portable tools that can be copied into any tscircuit project:
| Directory | Contents | Portable? |
|---|---|---|
tscircuit-scripts/ | DRC checks, placement validation, build error analysis, AV display | Templates β update paths for your project |
tscircuit-snapshot-tools/ | Review servers, gallery generator, review status I/O | Mostly β update group parsing regex |
To use these tools in a new project, copy from ~/.claude/skills/adom/tscircuit-scripts/ and ~/.claude/skills/adom/tscircuit-snapshot-tools/ into your project root.
For the full package.json scripts block: see project-setup-reference.md.
Starting State (Blank Template)
package.jsonwith name@tsci/adom-inc.CHANGEME, scripts for dev/build/snapshotindex.tsx- default export component (template placeholder)tscircuit.config.json- points mainEntrypoint tolib/index.tsx.npmrc- custom registry@tsci:registry=https://npm.tscircuit.combunfig.toml- bun config with lockfile save disabledtsconfig.json- TypeScript config with react-jsx, tscircuit types- Dependencies:
@tsci/adom-inc.library,@tsci/adom-inc.molecule,@tscircuit/mm
Steps
Step 1: Set the package name
- In
package.json, replaceCHANGEMEin"name": "@tsci/adom-inc.CHANGEME"with the actual component name - Ask the user what the name should be
- Format:
@tsci/adom-inc.<component-name> - Example:
@tsci/adom-inc.jst-connectors
Step 2: Create the lib folder
- All code goes in
/lib - Create
lib/index.tsxas the main entrypoint (matchestscircuit.config.jsonβ"mainEntrypoint": "lib/index.tsx") - Start with blank files, don't reuse template content from root
index.tsx
Step 3: Update dependencies
- Run
bun update --latestto update all packages to their latest versions - Do this every time you start working on a project
- Also run
bun add -d @types/bunto add bun types (needed for tsconfig"types": ["bun"]) - Peer dependency warnings about react/react-dom/circuit-json are normal and can be ignored
Step 4: Run the dev server
- Run
bun run start(which runstsci dev -p 3040) - This launches the tscircuit dev server on port 3040
- Use this to preview and test your components as you build them
Step 5: Create design-group .tsx files from data definitions
- Data definitions live in
lib/Connector_JST_Data/*/src/*_to_Generate.ts- arrays of connector specs - Each connector entry becomes its own
.tsxfile inlib/connector_design_groups/<Series>/ - Each .tsx exports a default component with a
<group>containing a<chip>using KiCad footprint - Use
footprint="kicad:LibraryName/FootprintName"syntax (see https://docs.tscircuit.com/footprints/kicad-footprints) - Derive KiCad footprint name from structured fields (partNumber, pitch, orientation) rather than transforming fileName
- Follow
reference_only/MoleculeTemplateGenerator.tspattern for the generator script - Generator goes in
scripts/folder, run withbun run scripts/<name>.ts - Generator should: validate unique fileNames, clean old generated files, write .tsx files, write barrel exports
- Follow tscircuit design-groups pattern from https://docs.tscircuit.com/guides/running-tscircuit/scripting/measuring-circuit-size
Step 6: Determine connector dimensions
- Each connector needs width/height dimensions (actual physical body size, NOT courtyard)
- General formula:
width = (pinsPerRow - 1) * pitch + overhang, height is fixed per series/orientation - For dual-row connectors, use
pinsPerRow = totalPins / 2 - How to derive: Use manufacturer datasheets (e.g. JST PDFs from jst.com/wp-content/uploads/)
- Look for the Header section's A/B dimension table: A = pin span, B = body width
- Overhang = B - A (constant for a given series/orientation/variant)
- Height = body depth on PCB (perpendicular to pin line) from the drawing
- Do NOT use KiCad courtyard (F.CrtYd) - it's larger than the actual component
- PDF extraction: download from jst.com, use
pdftotext -layoutto extract text
- Store dimension rules in generator as lookup table keyed by
series:orientation:variant - Export dimensions from each generated .tsx file:
export const dimensions = { width, height } - Some series have variant-specific dimensions (e.g., PH K vs SM4-TB have different overhangs)
Step 6b: Center connectors on board (pcbX offset)
- KiCad through-hole footprints have pin 1 at origin β connector is off-center
- KiCad surface mount footprints have center as origin β already centered
- For through-hole: add
pcbX={offset}to the<chip>whereoffset = -(pinsPerRow - 1) * pitch / 2 - For surface mount: no pcbX needed
- Add
centerOrigin: booleanto the DimensionRule type to track which is which - Generator calculates and emits
pcbXprop automatically for through-hole connectors - Dual-row connectors: the connector data MUST have
rows: 2β without it, the design group generator defaults torows ?? 1and uses total pins instead of pinsPerRow for width/pcbX calculations, causing massive offboard placement errors- The
generate-connector-design-groups.tsuses:const isDualRow = (connector.rows ?? 1) > 1 - If
rowsis missing, pcbX is wrong by(totalPins - pinsPerRow) * pitch / 2β e.g., 5mm off for a 2x05 at 2mm pitch
- The
Step 7: Combine design groups with Molecules to make boards
index.tsx(root) is the preview entry point - import a Molecule and place design groups inside it- The Molecule's inner working area is SMALLER than the declared size (mounting pins + wings eat into it)
- Use the connector's dimensions to pick the smallest Molecule size where the connector still fits
- Try reducing X/Y sizes until the connector just barely fits to minimize board waste
- Molecule sizes must be in multiples of 8: 8, 16, 24, 32, 40, 48, 56, 64... (e.g.
8x8,16x8,16x16,24x16) - Per-side wing overrides:
wingTop={N},wingBottom={N},wingLeft={N},wingRight={N}override the defaultwingvalue for that side only (e.g.,wingTop={2}extends only the top edge) - Use
bunx tsci build <file>to check for errors, add--pcb-svgs --schematic-svgs --glbsfor visual outputs - Center working area formula: for size
NxM, center margin β(N-2) x (M-2)mm - Connector courtyard must fit within the center working area
- Pick the smallest 8mm-increment size where center area fits the connector
- Example: 2-pin JST PH K vertical (5.9x4.5mm) β 8x8 works (6x6 center fits 5.9x4.5)
Step 8: Add machine contacts and traces per connector pin
There are two approaches depending on the component type:
Approach A: Manual contact placement (connectors with known pin positions)
- Each connector pin needs a corresponding MachineContactMedium and a trace connecting them
- Import
MachineContactMediumfrom@tsci/adom-inc.library - Place each contact at the exact X position of its connector pin, in the bottom margin (pcbY = -N where N = half the molecule nominal size)
- For an 8x8 molecule with 4 pins: bottom margin center is at pcbY=-4
- Contact pcbX values match the connector pin X positions
- For through-hole connectors with pcbX centering offset: pin positions are
pcbX_offset + (pinIndex * pitch)- Example: 2-pin PH K with pcbX=-1, pitch=2.0mm β pin1 at x=-1, pin2 at x=1
- Add a
<trace>for each pin connecting the contact to the connector pin - Traces use
width={1}β nopcbPathneeded (tscircuit auto-routes) - Pattern:
import { Molecule } from "@tsci/adom-inc.molecule" import { MachineContactMedium } from "@tsci/adom-inc.library" import { MyConnector } from "lib/connector_design_groups/..." export default () => ( <Molecule type="4pin" size="8x8" pinType="MachinePinMediumStandard" wing="nominal" roundEdges={true}> <MyConnector /> <MachineContactMedium name="MC1" pcbX={-1} pcbY={-4} /> <MachineContactMedium name="MC2" pcbX={1} pcbY={-4} /> <trace from="MC1.pin1" to="J1.pin1" width={1} /> <trace from="MC2.pin1" to="J1.pin2" width={1} /> </Molecule> ) - Contact naming: MC1, MC2, MC3... (one per connector pin)
- Trace format:
<trace from="MC{n}.pin1" to="J1.pin{n}" /> - Each MachineContactMedium has a single pin1 that connects to the corresponding connector pin
- Net naming: net names cannot start with numbers β use
VCC3V3not3V3,VCC5Vnot5V, etc.
Approach B: ValidateContacts (custom chip/IC components β PREFERRED)
For custom chip components (e.g., modules, ICs) that have named pins distributed across multiple sides, use <ValidateContacts> with individually-placed <MachineContactMedium> components. This gives precise control over contact positions, enabling clean routing by placing each contact near the IC pin it connects to.
import { Molecule, ValidateContacts } from "@tsci/adom-inc.molecule"
import { MachineContactMedium } from "@tsci/adom-inc.library"
import { MyChip } from "./MyChip"
export default () => (
<Molecule type="4pin" size="24x24" pinType="MachinePinMediumStandard" wing="nominal" roundEdges={true}>
<MyChip name="U1" />
<ValidateContacts allow1mmGrid>
{/* Left margin β aligned to left-side IC pins */}
<MachineContactMedium name="MC1" pcbX={-12} pcbY={2} />
<MachineContactMedium name="MC2" pcbX={-12} pcbY={0} />
<MachineContactMedium name="MC3" pcbX={-12} pcbY={-2} />
{/* Bottom margin β aligned to bottom-side IC pins */}
<MachineContactMedium name="MC4" pcbX={-3} pcbY={-12} />
<MachineContactMedium name="MC5" pcbX={-1} pcbY={-12} />
{/* Right margin β aligned to right-side IC pins */}
<MachineContactMedium name="MC6" pcbX={12} pcbY={-2} />
<MachineContactMedium name="MC7" pcbX={12} pcbY={-4} />
</ValidateContacts>
{/* Traces β each contact to its nearest IC pin */}
<trace from="MC1.pin1" to="U1.GND1" />
<trace from="MC2.pin1" to="U1.SCLK" />
<trace from="MC3.pin1" to="U1.GND2" />
<trace from="MC4.pin1" to="U1.CS" />
<trace from="MC5.pin1" to="U1.nIRQ" />
<trace from="MC6.pin1" to="U1.GPIO1" />
<trace from="MC7.pin1" to="U1.GPIO2" />
</Molecule>
)
Key rules for ValidateContacts placement:
- Position contacts near the IC pins they connect to β look at the IC footprint's SMT pad positions (pcbX/pcbY) and place the corresponding contact on the nearest margin at a similar Y (for left/right margins) or X (for top/bottom margins). This minimizes trace length and avoids crossing routes.
allow1mmGridβ enables odd-mm positions (e.g., -5, -3, -1, 1, 3, 5) in addition to the default 2mm grid. Avoid if possible β instead, move contacts to 2mm grid positions. Only useallow1mmGridas a last resort when pin positions genuinely cannot align to the 2mm grid.- Leave margins empty where needed β e.g., no contacts on the top row if the IC has an antenna area. Only place contacts where they're useful.
- Margin positions: for a 24x24 molecule (26.4mm board), margins are at pcbX=Β±12 (left/right) and pcbY=Β±12 (top/bottom). For other sizes, margin = half the nominal size.
- Minimum 2mm spacing between contacts on the same margin edge.
- Use
--routing-disabledfor first pass:bunx tsci build file.tsx --svgs --glbs --routing-disabledto verify placement before enabling the autorouter. - The chip component must include
cadModelwith STEP file for 3D export:cadModel={{ stepUrl: "./3d_models/model.step", rotationOffset: { x: 0, y: 0, z: 180 }, positionOffset: { x: 0, y: 2.85, z: 0 }, }}
Approach C: PackContacts (quick auto-placement)
For quick prototyping or when precise placement isn't critical, <PackContacts> auto-distributes contacts evenly across margins. However, this often produces poor routing because contacts may be far from their connected pins. Prefer ValidateContacts (Approach B) for production molecules.
import { Molecule, PackContacts } from "@tsci/adom-inc.molecule"
<PackContacts
leftMargin={["SCLK", "DATA_IN", "DATA_OUT"]}
rightMargin={["GPIO0", "GPIO1"]}
topMargin={["VIN", "VDDIO"]}
bottomMargin={["GND1", "GND2", "GND3"]}
/>
- MC numbering follows margin order: left first, then right, then top, then bottom
- Routing quality is often poor β contacts placed evenly without regard to IC pin positions
Step 9: Automate molecule generation
For full generator automation details β molecule sizing rules, connector positioning, horizontal layouts, asymmetric connectors, contact placement rules, build config, barrel exports, build commands, DRC (pre/post-build), placement validation, snapshots, and the full review/approval workflow: see generator-review-reference.md.
Summary of steps 9β11:
- Write generator in
scripts/generate-<series>-molecules.ts - Configure
tscircuit.config.jsonwithmainEntrypointandincludeBoardFiles - Build:
bunx tsci build --concurrency 4(usebunx tsci, NOTtsci) - Run DRC:
bun run drc(pre-build) andbun run drc:build(post-build) - Run placement check:
bun run placement [--series PH] - Snapshot:
bunx tsci snapshot --pcb-only --update --concurrency 4 lib/JST_Molecules - Launch review server:
bun run reviewβ port 3041 β approve/reject
<<<<<<< Updated upstream
CRITICAL: Any .tsx change invalidates its snapshot. Always re-snapshot and re-review.
- Vertical connectors:
minX = connWidth - 2,minY = connHeight - 2(datasheet body is smaller than silkscreen; fits in usable area which is ~size+2mm) - Horizontal connectors:
minX = connWidth,minY = connHeight(body genuinely extends to full dimensions) - Both: round up to next 8mm increment, minimum 8mm
- Horizontal body clearance check: after rounding to 8mm, if
molX - connWidth < 2, bump +8mm. This ensures at least 1mm clearance per side between the connector body edge and corner machine pin centers. Without this, connectors whose width is just under an 8mm boundary (e.g. 15.9mm in a 16mm molecule) overlap the corner machine pins. - Center-origin vertical body clearance check: for SMT/center-origin vertical connectors, the actual silkscreen extends beyond the nominal datasheet height. After rounding to 8mm, if
molY - connHeight < 2, bump Y +8mm. PasscenterOriginflag togetMoleculeSize()so this only applies to SMT variants (THT verticals with pin-1 at origin don't have this problem).- Example: PH SM4-TB vertical has nominal height 7.5mm but silkscreen extends ~8.6mm β Y=8 gives 0.5mm clearance (collision), Y=16 gives 8.5mm clearance (plenty of room).
- Key insight: vertical and horizontal connectors of the same pin count may need different molecule sizes β don't assume that a fix for one orientation applies to the other
Connector positioning
- THT horizontal (K variant): pin 1 at origin, body extends ~6mm below β add
pcbY={1}to shift up and reduce bottom overhang - SMT (SM4-TB variant): center origin, no shift needed
- Connector design groups must accept
(props: any)and spread onto<group {...props}>for positioning props to flow through
Horizontal connector layout (ideal spacing pattern)
- For connectors where signal pins are offset far from footprint origin (e.g. EH horizontal: pins at y=+7):
- Use 8mm Y molecule with contacts on bottom margin (pcbY = -cornerY)
- Push connector up with pcbY so body bottom sits ~1.4mm above contacts (ideal gap)
- Signal pins may overhang top of board β this is acceptable
- Example: EH horizontal with pcbY={-1} β body bottom at -2.6, contacts at -4 = 1.4mm gap
- Ideal clearance: ~1.4mm between connector body bottom edge and contact pads
- Test with a single temp file before regenerating all (e.g.
TEST_EH_Horizontal.tsx)
Asymmetric horizontal connectors with body overhang
- Some horizontal connectors have highly asymmetric bodies β the body extends mostly in one direction from the pin row
- Example: VH horizontal β body extends ~1.6mm toward contacts but ~14mm away from contacts
- Two strategies depending on overhang severity:
Strategy A: wingBottom (large overhang, e.g. VH B*PS bottom-entry)
- Use
wingBottom={N}on the Molecule to extend the board edge:<Molecule type="4pin" size="16x16" wingBottom={2} wing="nominal" ...> <MyConnector pcbY={4} /> <MachineContactMedium name="MC1" pcbX={-6} pcbY={8} /> </Molecule> wingBottom={2}extends the board 2mm below the bottom machine pins
Strategy B: Flush positioning (small overhang, e.g. VH S*P side-entry)
- Push connector up with fractional pcbY so body bottom aligns exactly with board edge β no wing needed:
<Molecule type="4pin" size="16x16" wing="nominal" ...> <MyConnector pcbY={4.3} /> <MachineContactMedium name="MC1" pcbX={-4} pcbY={8} /> </Molecule> - Connector pcbY can be non-integer (only contact pcbX/pcbY must be integers)
- Measure body extent from SVG: place connector, snapshot, calculate distance from origin to body bottom
- Set pcbY so body bottom = board bottom edge (cornerY + wing offset)
Sizing formula for asymmetric horizontal connectors:
HORIZ_BODY_NEAR_EXTENT= how far body extends past pin center toward contacts (~1.6mm for VH)connPcbY= how far to push connector toward contacts (e.g., 4mm for VH)minY = 2 * (connPcbY + HORIZ_BODY_NEAR_EXTENT + MIN_CORNER_CLEARANCE)β size for the near side- If using fractional pcbY for flush fit, use a rounded-down value for sizing calc to avoid over-sizing
- Tuning workflow: create a temp test file, snapshot it, visually verify clearances, then apply to generator
When same series has multiple horizontal sub-variants (e.g. VH: BPS bottom-entry vs SP side-entry):
- Differentiate in generator using
partPrefixor similar field - Each sub-variant may need different pcbY and wing strategy
- Use
isSideEntry = v.partPrefix === "S"pattern to branch logic
Contact placement rules
- Horizontal connectors with body overlap risk: all contacts on bottom margin (pcbY = -cornerY)
- Horizontal connectors (default): all contacts on top margin (pcbY = cornerY), away from body overhang
- Vertical connectors: alternate bottom/top margins (odd pins β -cornerY, even pins β +cornerY)
- Corner clearance: MIN_CORNER_CLEARANCE = 2mm β if a contact X is within 2mm of a corner machine pin, shift it inward
- Contact X must be integers: all contact pcbX values must be whole numbers (no decimals)
- Silkscreen labels for bottom-margin contacts (e.g., GND labels): place labels ABOVE the contact (higher pcbY), not below. Labels should be horizontal β do not apply pcbRotation.
- Uniform contact spacing: use
getContactXPositions()pattern β evenly distribute contacts with uniform integer spacing:- Default spacing =
Math.round(pitch)for ALL pin counts (e.g. 2.5mm pitch β 3mm spacing) - Even pin counts get slight asymmetry (<=1mm) via
Math.round(-(pins-1)*spacing/2)as start β this is acceptable - If outer contacts exceed available range (cornerX - 2mm clearance), shrink spacing by 1 and retry
- Example: 5-pin with cornerX=12 β -6, -3, 0, 3, 6 (spacing=3, symmetric)
- Example: 6-pin with cornerX=12 β -7, -4, -1, 2, 5, 8 (spacing=3, slight asymmetry)
- User preference: uniform spacing > perfect symmetry (slight asymmetry with even gaps is better than symmetric with uneven gaps)
- Default spacing =
Step 9b: Build configuration and barrel exports
tscircuit.config.jsonhas two key fields:mainEntrypoint: points tolib/index.ts(barrel export for all series)includeBoardFiles: glob patterns controlling which filestsci buildprocesses
- Barrel export (
lib/index.ts): re-exports all series index filesexport * from "./JST_Molecules/JST_EH/index" export * from "./JST_Molecules/JST_PH/index" // ... all series - Dev preview (
lib/index.tsx): separate file, imports one component fortsci dev - These are different files with different purposes β don't merge them
- Exclusion patterns in
includeBoardFiles:"!**/index.ts"β exclude barrel exports from board builds"!**/*_1MP_*"β exclude mounting pad variants that crash tscircuit core (see Known Limitations)
Step 9c: Building molecules
CRITICAL: What "ralph loop" actually means β the user must SEE every iteration. A ralph loop is not "edit β build β JSON-analyze β commit." JSON analysis alone is invisible to the user and feels like nothing is happening. Every iteration must produce a visual the user can look at, either via shotlog progression or an adom-tsci webview. If you do ten rebuilds without the user ever seeing a PCB render, you have not ralph-looped, you have just burned their time.
Required pattern for any non-trivial board work:
- Open a shotlog channel at the start of the loop β e.g.
nohup shotlog serve &thenshotlog inject --channel <board-name> --file /tmp/pcb.png --caption "iter 1: after supplier fix". Inject a fresh PNG after every rebuild. The user watches the channel to see the board evolve. - Build incrementally. Don't drop 300 lines of silkscreen in one step. Add one section β rebuild β screenshot β inject. Add next section β rebuild β inject. Each step shows progression.
- Show the in-progress board in an adom-tsci webview β either the canonical
adom-tsci start <dir>tab, or a dedicated second instance on a different port per build variant (adom-tsci start --port 8860 --tsci-port 3060 --no-openthenadom-cli hydrogen workspace add-tabwith a unique display name). Users want to side-by-side compare "before silkscreen vs after silkscreen."
NEVER cover the VS Code pane when opening any of these tabs. adom-tsci start and shotlog open both have unfiltered panel pickers and will happily park on top of VS Code, killing the chat window. Before opening ANY new Hydrogen tab, walk the layout and pick a non-VS-Code pane β full procedure in adom-workspace-control/SKILL.md under "Critical Rules". For shotlog open specifically (no --panel-id flag as of 2026-04): skip the wrapper, call adom-cli hydrogen workspace add-tab --panel-id <non-vscode-id> --url "https://<host>/proxy/8820/log/<channel>/" directly.
4. Inject a final PNG + final-state webview before asking the user to review. Never say "want me to commit?" without showing them the result first.
Silent builds + JSON-only analysis = invalid ralph loop, regardless of how many iterations you do. Treat shotlog / adom-tsci visibility as a hard requirement, not optional polish.
CRITICAL: Ralph-loop yourself before claiming anything works. Before telling the user "shotlog is ready, check it out" or "the webview shows the latest board", you must actually fetch the thing the user would see and confirm it isn't broken. The user doesn't want to be your QA team. Concrete self-test gates that MUST pass before each "look at this" hand-off:
- Shotlog images:
curl -sI "https://<wiki-host>/proxy/8820/shots/<channel>/<filename>"returnsHTTP/2 200andcontent-type: image/png. Also load the channel page and check<img>URLs resolve β a common failure mode is the shotlog server started in the wrong cwd and the file is saved under<project>/screenshots/shotlog/but the server is serving/home/adom/project/screenshots/shotlog/(or vice versa). A second failure mode:shotlog serveonly scans for new channels at startup, so a channel created after launch is invisible until you restart (kill <shotlog-pid>; nohup shotlog serve &). Verify with:curl -sI "http://127.0.0.1:8820/shots/<channel>/<file>"returns 200, THEN confirm the file on disk is under~/project/screenshots/shotlog/<channel>/(the path the server watches) β NOT in the tscircuit example dir. - adom-tsci webview tabs:
curl http://127.0.0.1:<port>/statereturnstsci_reachable: true. Check the build artifact timestamps underdist/lib/index/β if3d.glbis older than the latestlib/index.tsxedit, the 3D tab is showing a stale render. Re-runbunx tsci build lib/index.tsx --glbsto refresh, and also hitPOST http://127.0.0.1:<port>/reloadso open webviews invalidate their cache. - PNG integrity: any PNG you're about to inject or serve must be > 1KB and start with the PNG magic bytes.
head -c 8 file.png | od -An -cshould contain\211 P N G. Zero-byte and HTML-error-page "PNGs" are the classic shotlog failure mode.
If any check fails, fix before pinging the user. "Take a look at the shotlog" followed by "why is it broken?" is a failure state β it means you didn't actually look yourself first. A useful heuristic: if your fix involved restarting a server, copying files between dirs, or changing a URL path, you broke the self-test prerequisite and MUST re-run all three checks from scratch.
CRITICAL: Never hand manual busywork to the user. After rebuilding the board or updating silkscreen, don't say "reload the adom-tsci tab" or "refresh the shotlog page." Refresh it yourself β adom-cli hydrogen webview refresh --name "<tab name>" does it in one call. Same for activating tabs (workspace active-tab --name ...) and screenshotting panels for verification (screenshot panel --name ...). Full list of self-service actions and when to vs. when not to hand off is in adom-workspace-control/SKILL.md under "Critical Rules" β "NEVER ask the user to do something you can do yourself." Violation = wasted user time every turn.
CRITICAL: Never use Adom Viewer (AV). Always use Hydrogen Web View panels instead. AV is an optional Node service on port 8770 that the Babylon 3D viewer in adom-tsci depends on. If you build a ralph loop that expects AV, it fails silently the moment AV isn't running ("Adom Viewer not reachable on port 8770"), and the user sees a dead tab. Hydrogen Web View panels (panelType: adom/a1b2c3d4-0031-4000-a000-000000000031) are first-class, always-available, and render any URL β no external service dependency. Use them for shotlog, PCB SVG, schematic SVG, and anything else visual. Specific consequences for this skill:
- Don't open AV tabs (
av_display*,av_capture,av_tab_capture) as part of a ralph loop. - Don't direct the user to the "3D" tab of adom-tsci (it renders inside an AV-backed Babylon iframe). Use the PCB and Schematic tabs instead β those render SVGs in-page and need no external service.
- For 3D visualisation, generate a GLB and either (a) skip 3D for the loop, or (b) render it in a Web View that loads a standalone three.js page pointing at the GLB file via the tsci dev fileserver. Do NOT rely on the Babylon/AV pipeline.
- Never tell the user "start AV" or "open AV" β that's the same failure as "refresh the tab": it's manual setup they shouldn't have to do. Architect loops so they don't need AV.
CRITICAL: When pointing a Hydrogen Web View at adom-tsci (or shotlog, or the tsci dev server), never use a localhost: or 127.0.0.1: URL. The web view runs in the user's browser; container-local URLs are unreachable from there. Use the proxy template from $VSCODE_PROXY_URI β full rule + table of when to use localhost vs the proxy URL is in adom-workspace-control/SKILL.md under "Critical Rules." adom-tsci runs on port 8850 by default, tsci dev on 3040, shotlog on 8820 β all three must be wrapped in the proxy URL before going into webview navigate or add-tab --url.
CRITICAL: Always stream build progress so the user sees each phase β never run bunx tsci build silently. Builds regularly take 30-300s (ValidateContacts, autorouter, schematic solver, GLB render). Silent empty chat = "is it broken?" = user frustration. Always wrap builds in Monitor with a filter that emits every significant phase transition, not just errors.
Minimum filter β emits each phase with elapsed seconds so the user can watch time pass:
bunx tsci build lib/index.tsx --svgs 2>&1 | awk -v start=$(date +%s) '
/ValidateContacts|Building|Routing|Matchpack|schematic|Solver|Rendering|Wrote|Written|Circuits built|error|Error|Could not|Invalid|Fail|Killed|completed/ {
elapsed = systime() - start
printf "[+%3ds] %s\n", elapsed, $0
fflush()
}'
Do NOT use a narrow grep like grep -E "error|Written|completed" β it goes silent for minutes during ValidateContacts / routing and the user can't tell whether the build is alive or hung. If you are ralph-looping across many rebuilds, still show progress every time β users need to see the loop happening.
For progression viewing across multiple board iterations, open an adom-tsci webview per build step so the user can scrub through SVGs as they update (see Step 9c-multi below).
- CRITICAL: Always use
bunx tsci buildβ never usetsci export(legacy/limited) or baretsci(global CLI may be outdated) bunx tsciruns the project-local CLI fromnode_modules, which has the latest features and bug fixes- Full build:
bunx tsci build --concurrency 4 - Single series:
bunx tsci build lib/JST_Molecules/JST_PH - Single file:
bunx tsci build lib/JST_Molecules/JST_PH/Molecule_....tsx - Output goes to
dist/withcircuit.jsonper molecule in subdirectories:dist/lib/JST_Molecules/JST_PH/Molecule_X/circuit.json
--concurrency 4runs 4 builds in parallel (significantly faster)- Preview image flags:
--svgs(all SVGs),--pcb-svgs,--schematic-svgs,--glbsβ generate images from circuit.json after build --routing-disabledβ skips the autorouter entirely. Useful for first-pass placement verification: see contacts and component positions without waiting for routing. Enable routing once placement looks good.- Combined example:
bunx tsci build lib/Molecule_RM2.tsx --svgs --glbs --routing-disabled - BUG:
--pcb-svgswith folder builds: only generates 1 SVG then stops despite all circuit.json files being written. Usebunx tsci snapshotfor batch SVG generation instead. bunx tsci snapshot --pcb-only --update --concurrency 4 <path>β parallel snapshots (now supported, preferred for batch SVG generation)
Step 9d: Design Rules Checking (DRC)
- Pre-build DRC (
bun run drc): static analysis of generated.tsxfiles- Checks: auto-generated header, molecule size (8mm multiples), required props, sequential contact naming, integer pcbX/pcbY, corner clearance, uniform spacing, horizontal/vertical contact consistency, trace count/width/pcbPath/mapping, single connector, connector pcbY integer
- Also validates footprint strings against
kicad-autocomplete.tsin node_modules - CLI:
bun run drc [--series EH] [--verbose]
- Post-build DRC (
bun run drc:build): parsesdist/*/circuit.jsonfor runtime errors- Categorizes:
footprint_404(KiCad cache missing),trace_not_connected,other(trace overlaps etc.) - Requires
tsci buildto have been run first - CLI:
bun run drc:build [--series EH] [--verbose]
- Categorizes:
- Run pre-build DRC after generating molecules, post-build DRC after building
Step 9e: Placement validation
bun run placement: runsbunx tsci check placementon each molecule file (~9s/file)- This is a physical placement check using the full tscircuit rendering engine β catches issues that static DRC can't:
[offboard]β component extends past board edge[colliding]β components physically overlap[overlap]β pad overlap issuespadClearanceβ minimum distance between nearest pads (reports per component)
- Script:
scripts/check-placement.ts - CLI options:
--series PHβ check one series only--concurrency 4β parallel execution (default 4)
- Reports per-series pass/fail with min clearance, progress with ETA
- Exit code 1 on any failure β CI-friendly
- Note: much slower than
bun run drc(~9s per file vs instant), so use--seriesfor quick checks during development - Run after fixing generator bugs to verify placement is correct
Step 10: Snapshot and review with gallery
- Generate snapshots for all molecules:
bunx tsci snapshot --pcb-only --update <file> - Batch all snapshots with a shell loop over the molecule files
- Use
scripts/generate-snapshot-gallery.tsto build an HTML gallery page- Scans
__snapshots__/for*.snap.svgfiles (excludes.diff.svg) - Groups by sub-family (orientation x variant), sorted by pin count
- Outputs
gallery.htmlin the same__snapshots__/folder - Works offline β uses relative
<img src="./filename.svg">references - Dark background matching SVG style, responsive CSS grid, click-to-open
- Scans
- The molecule generator (
generate-ph-molecules.ts) callsgenerateGallery()at the end automatically - Open
gallery.htmlin browser to visually review all connectors at once - Look for: connector body overlapping machine pins, contacts overlapping each other, board overhang
Step 11: Review/approval workflow
- Separate system from the static gallery β local Bun web servers for approve/reject workflow
- Tools live in
snapshot-tools/(portable, no project-specific logic):global-review-server.tsβ unified multi-series review server with tab barreview-server.tsβ single-series review server (legacy, still works individually)review-status-io.tsβ shared types + read/write helpers forreview-status.jsongallery-generator.tsβ generates static HTML gallery from SVGs<snapshot-dir>/review-status.jsonβ persistent approval state per directory
Running the review server
- Global server (recommended):
bun run reviewβ port 3041- Auto-discovers all series from
lib/JST_Molecules/JST_*/__snapshots__/ - Landing page (
/) shows all series with snapshot counts and progress bars - Each series at
/{SERIES}(e.g.,/PH,/XH,/VH) β full review UI with tab bar - APIs scoped per series:
/api/{SERIES}/status,/api/{SERIES}/review, etc. - SVGs served at
/snapshots/{SERIES}/{filename} - Pre-commands derived from series name:
bun run scripts/generate-{series}-molecules.ts
- Auto-discovers all series from
- Single-series (legacy):
bun run review:ph/review:xhβ still available for focused work
Review UI features
- Dark theme CSS grid matching gallery style
- Tab bar (sticky top): all discovered series with snapshot counts and approval counts
- View modes (top-right toggle): Grid (multi-column thumbnails) and List (one per row, 480px-wide images for detailed inspection)
- Cards with snapshot image, label, status badge (green/red/gray)
- Per-card approve/reject buttons
- Section buttons: "Approve Section" / "Reject Section" in each group header
- Global buttons: "Approve All Visible" and "Reset All" (with custom confirmation modal)
- Filters: All / Pending / Approved / Rejected
- Lightbox: click card to enlarge, keyboard shortcuts:
A=approve,X=reject,P=pending, arrows=navigate,Esc=close, scroll=zoom, drag=pan, double-click=3x zoom - Regenerate Snapshots button: runs pre-command (generator) + snapshots all .tsx files with SSE progress
- Update Dependencies button: runs
bun update --latestwith live output - Sections group by orientation + connector variant + part pattern (e.g., "Vertical β VH (BP)", "Horizontal β VH (BPS)", "Horizontal β VH (S*P)")
- Part pattern extracted from molecule filename: prefix letters + suffix letters (e.g., B4PS β BPS, S3P β SP)
- This ensures different physical sub-variants within the same series appear in separate review sections
Pipeline integration (generator side)
- Generator reads
review-status.jsonbefore generating:- Approved: skip regeneration, preserve
.tsxfile, still include in barrel export - Rejected: regenerate, then reset status to "pending" for re-review
- Pending/missing: regenerate normally
- Approved: skip regeneration, preserve
- Clean step preserves approved
.tsxfiles - After generation, writes updated
review-status.json(rejectedβpending resets) - Import:
import { readReviewStatus, writeReviewStatus } from "../snapshot-tools/review-status-io" - Generator calls
generateGallery(snapshotDir)at the end for static gallery fallback
Developer workflow
bun run scripts/generate-{series}-molecules.tsβ generate molecules (skips approved)- Snapshot all molecules:
bunx tsci snapshot --pcb-only --update --concurrency 4 lib/JST_Molecules/JST_{SERIES} bun run reviewβ launch global review server (port 3041)- Navigate to
http://localhost:3041/{SERIES}β approve good, reject bad - Tell Claude "done reviewing" β Claude reads
review-status.json, checks rejections + notes, fixes generator - Re-run generator β only non-approved regenerated
- Re-snapshot, refresh review page β repeat until all approved
- OR: use the "Regenerate Snapshots" button in the UI to run steps 1+2 from the browser
CRITICAL RULE: Any .tsx change requires new snapshots + review
- Any modification to a molecule
.tsxfile invalidates its snapshot and approval status - If you modify a generator or directly edit
.tsxfiles, you MUST:- Re-run snapshots for all affected files
- Start a fresh review server (
bun run review) - Have the user review the changes
- This applies to ALL changes β even "minor" ones like removing
pcbPath, fixing spacing, changing traces, etc. - Never assume a previous approval still holds after a
.tsxfile has been modified - When in doubt: snapshot it, review it
Snapshot batch commands
- All molecules:
bunx tsci snapshot --pcb-only --update --concurrency 4 lib/JST_Molecules - Single series:
bunx tsci snapshot --pcb-only --update --concurrency 4 lib/JST_Molecules/JST_PH - Single file:
bunx tsci snapshot --pcb-only --update lib/JST_Molecules/JST_PH/<filename>.tsx --concurrency 4is ~4x faster than sequential
Stashed changes
Adom Viewer Integration
Full AV display guide, panzoom templates, mesh visibility, raw HTTP API, and tab-group patterns: see av-integration.md.
Quick start (unified script)
# Show all views (PCB + Schematic + 3D) as AV tabs
bun scripts/show-in-av.ts lib/index.tsx
# Show individual views
bun scripts/show-in-av.ts lib/index.tsx pcb # PCB layout SVG
bun scripts/show-in-av.ts lib/index.tsx sch # Schematic SVG
bun scripts/show-in-av.ts lib/index.tsx 3d # 3D model (Basic3DView)
# Works with any .tsx component, not just lib/index.tsx
bun scripts/show-in-av.ts lib/JST_Molecules/JST_PH/Molecule_JST_PH_B2B_PH_K_1x02_P2_00mm_Vertical.tsx
Important: Always use bunx tsci instead of bare tsci to run the project-local CLI.
Build output layout
dist/
<input-path-without-ext>/
circuit.json # Circuit JSON
pcb.svg # PCB SVG
schematic.svg # Schematic SVG
3d.glb # 3D GLB with embedded STEP models
Displaying build outputs β av tsci CLI (THE ONLY WAY)
Use av tsci. Do NOT try to build your own display pipeline.
Common mistakes (all wrong):
av_display_filefor SVGs β static, no pan/zoom, no tab groupingav_basic_3d_displayfor GLB β doesn't support tab groups, creates ungrouped tab- Pushing 3 separate tabs without
groupβ not linked as a cohesive set - Building a custom
serve-viewspipeline β unnecessary whenav tsciexists
The correct approach:
# 1. Check if av CLI is installed
ls ~/.local/bin/av || ls ~/gallia/viewer/cli/av
# 2. Install if missing
cp ~/gallia/viewer/cli/av ~/.local/bin/av && chmod +x ~/.local/bin/av
# 3. One command β that's it
av tsci dist/index --name "SN65HVD230"
The av tsci command:
- Automatically closes existing tabs in the same group before pushing β no duplicates
- Auto-detects
*schematic*.svg,*pcb*.svg, and*.glbin the directory - Pushes all three as a grouped tab set with proper panzoom wrappers for SVGs
- Uses correct 3D orientation for tscircuit GLBs
- Handles all the content types, group names, and source flags correctly
- Safe to call repeatedly β each call replaces the previous group cleanly
# Full av CLI reference
av tsci <dir> [--name N] # push 3-tab group (replaces existing)
av switch <index> # switch to tab by index
av list # list all tabs with index and group
av close --title "PCB" # close tabs by title
av close --source "tscircuit" # close tabs by source
av close --group "SN65HVD230" # close entire group
av clear # close ALL tabs
av status # check viewer connection
av push <file> [--title T] [--group G] # push single file as tab
av 3d <file.glb> [--title T] [--group G] # push 3D model
av reload # force viewer page reload
For full details (mini web server, legacy serve-views.js, 3D camera settings, tab groups, panzoom template, mesh controls, raw HTTP API): see av-integration.md.
Datasheet Research and IC Molecule Workflow
Datasheet reading guidelines, passive placement principles, placement workflow, and full IC molecule workflow (footprint import, pin labels, orientation, decoupling, contact placement, iterative build loop): see ic-molecule-reference.md.
DRC & Validation Commands
DRC subcommands, error categories, courtyard SVG generation, PCB features (copper pours, courtyard outlines, vias), AV CLI, valid molecule sizes, known limitations, and troubleshooting table: see pcb-drc-reference.md.