πŸ’¬ Sample prompts Paste any of these into Claude Code to use this skill
Tscircuit project Start a new tscircuit project for an RP2040 board
Build and view Build my tscircuit project and open the 3D viewer
Export gerbers Export gerbers from this tscircuit project
Walkthrough Make a walkthrough demo for my tscircuit board
Add component Add a BME680 to my tscircuit schematic
⚑ Install this skill

Paste this into Claude Code (VS Code panel, Adom editor, or terminal) to install:

Search the Adom Wiki for the skill "adom tscircuit" (slug: adom-tscircuit-skill) at https://wiki-ufypy5dpx93o.adom.cloud/wiki/skills/adom-tscircuit-skill and install it into my local ~/.claude/skills/adom-tscircuit-skill/ directory. Fetch the skill_source content from the wiki page and save it as SKILL.md. Then confirm it's installed by showing the first 5 lines.
?
What is a skill? Skills are instructions that teach AI assistants like Claude Code how to perform specific tasks. The description below is loaded into the AI as context when you invoke this skill. Well-written skills make the AI significantly more effective. Like Wikipedia, anyone can improve a skill by clicking Edit AI Skill — or have your AI submit an edit on your behalf.

Description

Edit AI Skill

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 flags
  • SYNTAX.md β€” tscircuit JSX syntax reference
  • WORKFLOW.md β€” general tscircuit workflow patterns
  • CHECKLIST.md β€” pre-export validation checklist
  • IMPORT-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:

DirectoryContentsPortable?
tscircuit-scripts/DRC checks, placement validation, build error analysis, AV displayTemplates β€” update paths for your project
tscircuit-snapshot-tools/Review servers, gallery generator, review status I/OMostly β€” 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.json with name @tsci/adom-inc.CHANGEME, scripts for dev/build/snapshot
  • index.tsx - default export component (template placeholder)
  • tscircuit.config.json - points mainEntrypoint to lib/index.tsx
  • .npmrc - custom registry @tsci:registry=https://npm.tscircuit.com
  • bunfig.toml - bun config with lockfile save disabled
  • tsconfig.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, replace CHANGEME in "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.tsx as the main entrypoint (matches tscircuit.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 --latest to update all packages to their latest versions
  • Do this every time you start working on a project
  • Also run bun add -d @types/bun to 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 runs tsci 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 .tsx file in lib/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.ts pattern for the generator script
  • Generator goes in scripts/ folder, run with bun 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 -layout to 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> where offset = -(pinsPerRow - 1) * pitch / 2
  • For surface mount: no pcbX needed
  • Add centerOrigin: boolean to the DimensionRule type to track which is which
  • Generator calculates and emits pcbX prop automatically for through-hole connectors
  • Dual-row connectors: the connector data MUST have rows: 2 β€” without it, the design group generator defaults to rows ?? 1 and uses total pins instead of pinsPerRow for width/pcbX calculations, causing massive offboard placement errors
    • The generate-connector-design-groups.ts uses: const isDualRow = (connector.rows ?? 1) > 1
    • If rows is missing, pcbX is wrong by (totalPins - pinsPerRow) * pitch / 2 β€” e.g., 5mm off for a 2x05 at 2mm pitch

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 default wing value 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 --glbs for 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 MachineContactMedium from @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} β€” no pcbPath needed (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 VCC3V3 not 3V3, VCC5V not 5V, 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 use allow1mmGrid as 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-disabled for first pass: bunx tsci build file.tsx --svgs --glbs --routing-disabled to verify placement before enabling the autorouter.
  • The chip component must include cadModel with 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:

  1. Write generator in scripts/generate-<series>-molecules.ts
  2. Configure tscircuit.config.json with mainEntrypoint and includeBoardFiles
  3. Build: bunx tsci build --concurrency 4 (use bunx tsci, NOT tsci)
  4. Run DRC: bun run drc (pre-build) and bun run drc:build (post-build)
  5. Run placement check: bun run placement [--series PH]
  6. Snapshot: bunx tsci snapshot --pcb-only --update --concurrency 4 lib/JST_Molecules
  7. 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. Pass centerOrigin flag to getMoleculeSize() 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 partPrefix or 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)

Step 9b: Build configuration and barrel exports

  • tscircuit.config.json has two key fields:
    • mainEntrypoint: points to lib/index.ts (barrel export for all series)
    • includeBoardFiles: glob patterns controlling which files tsci build processes
  • Barrel export (lib/index.ts): re-exports all series index files
    export * 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 for tsci 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:

  1. Open a shotlog channel at the start of the loop β€” e.g. nohup shotlog serve & then shotlog 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.
  2. 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.
  3. 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-open then adom-cli hydrogen workspace add-tab with 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:

  1. Shotlog images: curl -sI "https://<wiki-host>/proxy/8820/shots/<channel>/<filename>" returns HTTP/2 200 and content-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 serve only 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.
  2. adom-tsci webview tabs: curl http://127.0.0.1:<port>/state returns tsci_reachable: true. Check the build artifact timestamps under dist/lib/index/ β€” if 3d.glb is older than the latest lib/index.tsx edit, the 3D tab is showing a stale render. Re-run bunx tsci build lib/index.tsx --glbs to refresh, and also hit POST http://127.0.0.1:<port>/reload so open webviews invalidate their cache.
  3. 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 -c should 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 use tsci export (legacy/limited) or bare tsci (global CLI may be outdated)
  • bunx tsci runs the project-local CLI from node_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/ with circuit.json per molecule in subdirectories:
    • dist/lib/JST_Molecules/JST_PH/Molecule_X/circuit.json
  • --concurrency 4 runs 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-svgs with folder builds: only generates 1 SVG then stops despite all circuit.json files being written. Use bunx tsci snapshot for 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 .tsx files
    • 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.ts in node_modules
    • CLI: bun run drc [--series EH] [--verbose]
  • Post-build DRC (bun run drc:build): parses dist/*/circuit.json for runtime errors
    • Categorizes: footprint_404 (KiCad cache missing), trace_not_connected, other (trace overlaps etc.)
    • Requires tsci build to have been run first
    • CLI: bun run drc:build [--series EH] [--verbose]
  • Run pre-build DRC after generating molecules, post-build DRC after building

Step 9e: Placement validation

  • bun run placement: runs bunx tsci check placement on 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 issues
    • padClearance β€” 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 --series for 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.ts to build an HTML gallery page
    • Scans __snapshots__/ for *.snap.svg files (excludes .diff.svg)
    • Groups by sub-family (orientation x variant), sorted by pin count
    • Outputs gallery.html in the same __snapshots__/ folder
    • Works offline β€” uses relative <img src="./filename.svg"> references
    • Dark background matching SVG style, responsive CSS grid, click-to-open
  • The molecule generator (generate-ph-molecules.ts) calls generateGallery() at the end automatically
  • Open gallery.html in 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 bar
    • review-server.ts β€” single-series review server (legacy, still works individually)
    • review-status-io.ts β€” shared types + read/write helpers for review-status.json
    • gallery-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
  • 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 --latest with 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.json before generating:
    • Approved: skip regeneration, preserve .tsx file, still include in barrel export
    • Rejected: regenerate, then reset status to "pending" for re-review
    • Pending/missing: regenerate normally
  • Clean step preserves approved .tsx files
  • 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

  1. bun run scripts/generate-{series}-molecules.ts β€” generate molecules (skips approved)
  2. Snapshot all molecules:
    bunx tsci snapshot --pcb-only --update --concurrency 4 lib/JST_Molecules/JST_{SERIES}
    
  3. bun run review β€” launch global review server (port 3041)
  4. Navigate to http://localhost:3041/{SERIES} β€” approve good, reject bad
  5. Tell Claude "done reviewing" β€” Claude reads review-status.json, checks rejections + notes, fixes generator
  6. Re-run generator β†’ only non-approved regenerated
  7. 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 .tsx file invalidates its snapshot and approval status
  • If you modify a generator or directly edit .tsx files, you MUST:
    1. Re-run snapshots for all affected files
    2. Start a fresh review server (bun run review)
    3. 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 .tsx file 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 4 is ~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_file for SVGs β€” static, no pan/zoom, no tab grouping
  • av_basic_3d_display for 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-views pipeline β€” unnecessary when av tsci exists

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 *.glb in 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.

Skill Source

Edit AI Skill
---
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 flags
- `SYNTAX.md` β€” tscircuit JSX syntax reference
- `WORKFLOW.md` β€” general tscircuit workflow patterns
- `CHECKLIST.md` β€” pre-export validation checklist
- `IMPORT-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.**

```tsx
// 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

```tsx
import { Molecule, ValidateContacts } from "@tsci/adom-inc.molecule"
import { MachineContactMedium } from "@tsci/adom-inc.library"
```

### 3. Minimal molecule structure

```tsx
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.

```bash
# 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:
```bash
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.

```bash
# 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:
```text
@tsci:registry=https://npm.tscircuit.com
```

If `bun install` fails with a 401/403 error (unlikely with public dist), authenticate:
```bash
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`](project-setup-reference.md).

```bash
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):
```bash
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`](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`](project-setup-reference.md).

## Starting State (Blank Template)

- `package.json` with name `@tsci/adom-inc.CHANGEME`, scripts for dev/build/snapshot
- `index.tsx` - default export component (template placeholder)
- `tscircuit.config.json` - points mainEntrypoint to `lib/index.tsx`
- `.npmrc` - custom registry `@tsci:registry=https://npm.tscircuit.com`
- `bunfig.toml` - bun config with lockfile save disabled
- `tsconfig.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`, replace `CHANGEME` in `"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.tsx` as the main entrypoint (matches `tscircuit.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 --latest` to update all packages to their latest versions
- Do this every time you start working on a project
- Also run `bun add -d @types/bun` to 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 runs `tsci 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 `.tsx` file in `lib/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.ts` pattern for the generator script
- Generator goes in `scripts/` folder, run with `bun 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 -layout` to 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>` where `offset = -(pinsPerRow - 1) * pitch / 2`
- For surface mount: no pcbX needed
- Add `centerOrigin: boolean` to the DimensionRule type to track which is which
- Generator calculates and emits `pcbX` prop automatically for through-hole connectors
- **Dual-row connectors**: the connector data MUST have `rows: 2` β€” without it, the design group generator defaults to `rows ?? 1` and uses total pins instead of pinsPerRow for width/pcbX calculations, causing massive offboard placement errors
  - The `generate-connector-design-groups.ts` uses: `const isDualRow = (connector.rows ?? 1) > 1`
  - If `rows` is missing, pcbX is wrong by `(totalPins - pinsPerRow) * pitch / 2` β€” e.g., 5mm off for a 2x05 at 2mm pitch

### 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 default `wing` value 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 --glbs` for 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 `MachineContactMedium` from `@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}` β€” no `pcbPath` needed (tscircuit auto-routes)
- Pattern:
  ```tsx
  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 `VCC3V3` not `3V3`, `VCC5V` not `5V`, 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.

```tsx
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 use `allow1mmGrid` as 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-disabled` for first pass**: `bunx tsci build file.tsx --svgs --glbs --routing-disabled` to verify placement before enabling the autorouter.
- The chip component must include `cadModel` with STEP file for 3D export:
  ```tsx
  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.**

```tsx
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`](generator-review-reference.md).

Summary of steps 9–11:
1. Write generator in `scripts/generate-<series>-molecules.ts`
2. Configure `tscircuit.config.json` with `mainEntrypoint` and `includeBoardFiles`
3. Build: `bunx tsci build --concurrency 4` (use `bunx tsci`, NOT `tsci`)
4. Run DRC: `bun run drc` (pre-build) and `bun run drc:build` (post-build)
5. Run placement check: `bun run placement [--series PH]`
6. Snapshot: `bunx tsci snapshot --pcb-only --update --concurrency 4 lib/JST_Molecules`
7. 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. Pass `centerOrigin` flag to `getMoleculeSize()` 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:
  ```tsx
  <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:
  ```tsx
  <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: B*PS bottom-entry vs S*P side-entry):
- Differentiate in generator using `partPrefix` or 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)

### Step 9b: Build configuration and barrel exports

- `tscircuit.config.json` has two key fields:
  - `mainEntrypoint`: points to `lib/index.ts` (barrel export for all series)
  - `includeBoardFiles`: glob patterns controlling which files `tsci build` processes
- **Barrel export** (`lib/index.ts`): re-exports all series index files
  ```typescript
  export * 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 for `tsci 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:

1. **Open a shotlog channel** at the start of the loop β€” e.g. `nohup shotlog serve &` then `shotlog 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.
2. **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.
3. **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-open` then `adom-cli hydrogen workspace add-tab` with 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:

1. **Shotlog images**: `curl -sI "https://<wiki-host>/proxy/8820/shots/<channel>/<filename>"` returns `HTTP/2 200` and `content-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 serve` only 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.
2. **adom-tsci webview tabs**: `curl http://127.0.0.1:<port>/state` returns `tsci_reachable: true`. Check the build artifact timestamps under `dist/lib/index/` β€” if `3d.glb` is older than the latest `lib/index.tsx` edit, the 3D tab is showing a stale render. Re-run `bunx tsci build lib/index.tsx --glbs` to refresh, and also hit `POST http://127.0.0.1:<port>/reload` so open webviews invalidate their cache.
3. **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 -c` should 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:

```bash
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 use `tsci export` (legacy/limited) or bare `tsci` (global CLI may be outdated)
- `bunx tsci` runs the project-local CLI from `node_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/` with `circuit.json` per molecule in subdirectories:
  - `dist/lib/JST_Molecules/JST_PH/Molecule_X/circuit.json`
- `--concurrency 4` runs 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-svgs` with folder builds**: only generates 1 SVG then stops despite all circuit.json files being written. Use `bunx tsci snapshot` for 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 `.tsx` files
  - 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.ts` in node_modules
  - CLI: `bun run drc [--series EH] [--verbose]`
- **Post-build DRC** (`bun run drc:build`): parses `dist/*/circuit.json` for runtime errors
  - Categorizes: `footprint_404` (KiCad cache missing), `trace_not_connected`, `other` (trace overlaps etc.)
  - Requires `tsci build` to have been run first
  - CLI: `bun run drc:build [--series EH] [--verbose]`
- Run pre-build DRC after generating molecules, post-build DRC after building

### Step 9e: Placement validation

- **`bun run placement`**: runs `bunx tsci check placement` on 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 issues
  - `padClearance` β€” 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 `--series` for 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.ts` to build an HTML gallery page
  - Scans `__snapshots__/` for `*.snap.svg` files (excludes `.diff.svg`)
  - Groups by sub-family (orientation x variant), sorted by pin count
  - Outputs `gallery.html` in the same `__snapshots__/` folder
  - Works offline β€” uses relative `<img src="./filename.svg">` references
  - Dark background matching SVG style, responsive CSS grid, click-to-open
- The molecule generator (`generate-ph-molecules.ts`) calls `generateGallery()` at the end automatically
- Open `gallery.html` in 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 bar
  - `review-server.ts` β€” single-series review server (legacy, still works individually)
  - `review-status-io.ts` β€” shared types + read/write helpers for `review-status.json`
  - `gallery-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`
- **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 --latest` with live output
- Sections group by orientation + connector variant + part pattern (e.g., "Vertical β€” VH (B*P)", "Horizontal β€” VH (B*PS)", "Horizontal β€” VH (S*P)")
- Part pattern extracted from molecule filename: prefix letters + suffix letters (e.g., B4PS β†’ B*PS, S3P β†’ S*P)
- This ensures different physical sub-variants within the same series appear in separate review sections

#### Pipeline integration (generator side)

- Generator reads `review-status.json` before generating:
  - **Approved**: skip regeneration, preserve `.tsx` file, still include in barrel export
  - **Rejected**: regenerate, then reset status to "pending" for re-review
  - **Pending/missing**: regenerate normally
- Clean step preserves approved `.tsx` files
- 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

1. `bun run scripts/generate-{series}-molecules.ts` β€” generate molecules (skips approved)
2. Snapshot all molecules:
   ```bash
   bunx tsci snapshot --pcb-only --update --concurrency 4 lib/JST_Molecules/JST_{SERIES}
   ```
3. `bun run review` β€” launch global review server (port 3041)
4. Navigate to `http://localhost:3041/{SERIES}` β€” approve good, reject bad
5. Tell Claude "done reviewing" β€” Claude reads `review-status.json`, checks rejections + notes, fixes generator
6. Re-run generator β†’ only non-approved regenerated
7. 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 `.tsx` file invalidates its snapshot and approval status**
- If you modify a generator or directly edit `.tsx` files, you MUST:
  1. Re-run snapshots for all affected files
  2. Start a fresh review server (`bun run review`)
  3. 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 `.tsx` file 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 4` is ~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`](av-integration.md).

### Quick start (unified script)

```bash
# 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

```text
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_file` for SVGs β€” static, no pan/zoom, no tab grouping
- `av_basic_3d_display` for 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-views` pipeline β€” unnecessary when `av tsci` exists

**The correct approach:**

```bash
# 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 `*.glb` in 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

```bash
# 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`](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`](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`](pcb-drc-reference.md).

Sub-Skills
?
What are Sub-Skills?

Sub-skills are community-contributed AI skill extensions for this component. They teach AI assistants about specific tools, configurators, or workflows.

Examples:

  • A manufacturer’s configuration tool for a motor controller
  • A community-written design guide for an amplifier circuit
  • An automated test/validation script for a sensor module

How to add one: Click Add Sub-Skill, provide the URL to your skill and a brief description. Submissions are reviewed by the Adom team before going live.

No sub-skills yet. Be the first to contribute one!

Recent activity

1 commit
  • ✎
    Edit v1.0.0 John Lauer 26 days ago
    Sync with gallia β€” split reference content into sibling files (progressive disclosure) and cleanup pass on descriptions
0 revisions · Updated 2026-05-01 14:14:19