ShiboSoftwareDev/solar-battery-charger

This code defines various surface-mount and through-hole electronic components (like diodes, voltage regulators, and ICs) with detailed footprints, 3D models, and pin configurations for PCB design.

Version
1.0.2
License
unset
Stars
0

REPROS.md

# Repros for IC1 Routing Issues

These are intended as small starting points for `@tscircuit/core` regression tests.
They avoid explicit `pcbPath` coordinates and focus on routing input/output behavior.

## 1. Explicit trace thickness is clamped by board min trace width

### Problem

Two explicit traces connect pads to the same net and both request `0.06mm`.
The generated source traces preserve `min_trace_thickness: 0.06`, but the emitted
PCB route is widened to the board/router minimum, e.g. `0.15mm`.

On the solar charger board this is the `IC1.B2 -> net.SCL` and
`J4.SCL -> net.SCL` connection. The center pad trace is visually thick even
though both source traces specify `thickness="0.06mm"`.

### Reduced JSX

```tsx
import React from "react"

const OnePad = ({ name, pcbX, pcbY }: { name: string; pcbX: number; pcbY: number }) => (
  <chip
    name={name}
    pcbX={pcbX}
    pcbY={pcbY}
    pinLabels={{ pin1: ["P"] }}
    footprint={
      <footprint>
        <smtpad
          portHints={["pin1", "P"]}
          pcbX="0mm"
          pcbY="0mm"
          width="0.25mm"
          height="0.25mm"
          shape="rect"
        />
      </footprint>
    }
  />
)

export default () => (
  <board width="20mm" height="10mm" minTraceWidth="0.15mm">
    <OnePad name="U1" pcbX={-4} pcbY={0} />
    <OnePad name="J1" pcbX={4} pcbY={0} />

    <trace from="U1.P" to="net.SCL" thickness="0.06mm" />
    <trace from="J1.P" to="net.SCL" thickness="0.06mm" />
  </board>
)
```

### Expected

The single routed `net.SCL` PCB trace should use the explicit net trace width:

```ts
expect(uniqueWireWidthsForNet("SCL")).toEqual([0.06])
```

### Actual symptom

The route is emitted at the board/router minimum:

```ts
expect(uniqueWireWidthsForNet("SCL")).toEqual([0.15])
```

### Why this looks like core

`source_trace.min_trace_thickness` is correctly present. The issue appears when
core creates/applies routing connections: the net route width gets clamped by
the board minimum instead of preserving the explicit trace thickness.

Relevant behavior observed in local `@tscircuit/core`:

- source traces carry `min_trace_thickness: 0.06`
- net routing connection initially sees `width: 0.06`
- DRC/min-width application raises it to the board/router minimum

The desired behavior is probably: explicit `trace.thickness` should be a route
width request for that connection, while board `minTraceWidth` should be a DRC
constraint or fallback, not an override for explicit narrow traces.

## 2. Multi-port net emits an orphan/cut trace segment

### Problem

A multi-port net can emit an extra PCB trace fragment with no real source trace
and no port-connected endpoints. On the solar charger board this appears as a
short cut segment at the bottom right:

```txt
Trace [trace[source_net_29_mst1_0]] has disconnected endpoint at (-5.70, -12.55)
Trace [trace[source_net_29_mst1_0]] has disconnected endpoint at (-5.28, -12.55)
```

In the generated circuit JSON, the bad fragment looked like:

```json
{
  "type": "pcb_trace",
  "pcb_trace_id": "source_net_29_mst1_0",
  "connection_name": "source_net_29",
  "source_trace_id": undefined,
  "route": [
    { "route_type": "wire", "x": -5.7, "y": -12.55002, "width": 0.1, "layer": "top" },
    { "route_type": "wire", "x": -5.281, "y": -12.55002, "width": 0.1, "layer": "top" }
  ]
}
```

### Reduced JSX

This reproduces the same shape of input: four independent stubs into one net,
with three local output-stage pads and one far connector pad.

```tsx
import React from "react"

const OnePad = ({ name, pcbX, pcbY }: { name: string; pcbX: number; pcbY: number }) => (
  <chip
    name={name}
    pcbX={pcbX}
    pcbY={pcbY}
    pinLabels={{ pin1: ["P"] }}
    footprint={
      <footprint>
        <smtpad
          portHints={["pin1", "P"]}
          pcbX="0mm"
          pcbY="0mm"
          width="1mm"
          height="1mm"
          shape="rect"
        />
      </footprint>
    }
  />
)

export default () => (
  <board width="34.8mm" height="30mm" autorouterVersion="v4">
    <OnePad name="U1_OUTPUT" pcbX={0.495} pcbY={-13.25} />
    <OnePad name="L2_OUT" pcbX={-8.4} pcbY={-12.205} />
    <OnePad name="C_VOLT2_POS" pcbX={-5.5} pcbY={-10.857} />
    <OnePad name="J2_VOUT" pcbX={16} pcbY={11.3} />

    <trace from="U1_OUTPUT.P" to="net.VOUT" />
    <trace from="L2_OUT.P" to="net.VOUT" />
    <trace from="C_VOLT2_POS.P" to="net.VOUT" />
    <trace from="J2_VOUT.P" to="net.VOUT" />
  </board>
)
```

### Expected

All emitted PCB traces for `net.VOUT` should have at least one source trace or
valid port endpoint ownership, and there should be no disconnected-endpoint
errors:

```ts
const voutTraces = getPcbTracesForNet("VOUT")

expect(voutTraces.every((trace) => trace.source_trace_id)).toBe(true)
expect(errors.map((e) => e.message)).not.toContain("disconnected endpoint")
```

More directly, no emitted route should be just an unanchored two-point wire:

```ts
expect(
  voutTraces.some(
    (trace) =>
      !trace.source_trace_id &&
      trace.route.length === 2 &&
      !trace.route.some((p) => p.start_pcb_port_id || p.end_pcb_port_id),
  ),
).toBe(false)
```

### Actual symptom

On the solar charger board, the router/core output includes an orphan route
similar to:

```ts
{
  pcb_trace_id: "source_net_29_mst1_0",
  source_trace_id: undefined,
  route: [
    { x: -5.70, y: -12.55, width: 0.1, layer: "top" },
    { x: -5.28, y: -12.55, width: 0.1, layer: "top" },
  ],
}
```

### Where to look

This may be autorouter output or core route-output conversion:

- If the autorouter output already contains the orphan two-point segment, fix
  the autorouter.
- If autorouter output is clean and core creates `source_net_29_mst1_0` while
  converting to `pcb_trace`, fix core.

The core-side regression test should capture the simple-route-json sent to the
autorouter and the route result before conversion, then assert where the orphan
first appears.