diff --git a/test-assets/sample024-mp1-gap-fill.json b/test-assets/sample024-mp1-gap-fill.json
new file mode 100644
index 0000000..678de2a
--- /dev/null
+++ b/test-assets/sample024-mp1-gap-fill.json
@@ -0,0 +1,91 @@
+{
+ "source": "@tscircuit/dataset-srj24/samples/sample024.json / MP1.4",
+ "bounds": {
+ "minX": 16.57679492,
+ "maxX": 305.8762,
+ "minY": 37.96359746,
+ "maxY": 172.88179491999998
+ },
+ "boardVoid": {
+ "layerCount": 8,
+ "boardVoidRects": [
+ {
+ "x": 16.57679492,
+ "y": 38.9636,
+ "width": 1.0000050799999975,
+ "height": 1.6409653693827266
+ }
+ ]
+ },
+ "meshNodes": [
+ {
+ "capacityMeshNodeId": "cmn_237",
+ "center": {
+ "x": 28.774499999999996,
+ "y": 40.866699499999996
+ },
+ "width": 11.082599999999996,
+ "height": 3.8061989999999994,
+ "layer": "top",
+ "availableZ": [0, 1, 2, 3, 4, 5, 6, 7]
+ },
+ {
+ "capacityMeshNodeId": "cmn_5235",
+ "center": {
+ "x": 20.733199579999997,
+ "y": 42.37039786
+ },
+ "width": 5.000000159999999,
+ "height": 5.000000159999999,
+ "layer": "top",
+ "availableZ": [0, 1, 2, 3, 4, 5, 6, 7],
+ "_containsObstacle": true,
+ "_containsTarget": true,
+ "_connectedTo": [
+ "pcb_plated_hole_altium_6144",
+ "connectivity_net13098",
+ "pcb_trace_altium_31391",
+ "pcb_trace_altium_31390",
+ "pcb_plated_hole_altium_6244",
+ "pcb_plated_hole_altium_6242",
+ "pcb_plated_hole_altium_6239",
+ "pcb_plated_hole_altium_6238",
+ "pcb_plated_hole_altium_6222",
+ "pcb_plated_hole_altium_6183",
+ "pcb_plated_hole_altium_6172",
+ "pcb_plated_hole_altium_6147",
+ "pcb_plated_hole_altium_6146",
+ "pcb_plated_hole_altium_6145",
+ "source_trace_altium_pcb_307",
+ "source_port_altium_6144",
+ "source_port_altium_6145",
+ "source_port_altium_6146",
+ "source_port_altium_6147",
+ "source_port_altium_6172",
+ "source_port_altium_6183",
+ "source_port_altium_6222",
+ "source_port_altium_6238",
+ "source_port_altium_6239",
+ "source_port_altium_6242",
+ "source_port_altium_6244",
+ "source_net_altium_pcb_307",
+ "pcb_port_altium_6144",
+ "pcb_port_altium_6145",
+ "pcb_port_altium_6146",
+ "pcb_port_altium_6147",
+ "pcb_port_altium_6172",
+ "pcb_port_altium_6183",
+ "pcb_port_altium_6222",
+ "pcb_port_altium_6238",
+ "pcb_port_altium_6239",
+ "pcb_port_altium_6242",
+ "pcb_port_altium_6244"
+ ]
+ }
+ ],
+ "viaCenter": {
+ "x": 20.404997829999996,
+ "y": 39.41699889
+ },
+ "viaDiameter": 0.3
+}
diff --git a/tests/__snapshots__/sample024-gap-via-repro.snap.svg b/tests/__snapshots__/sample024-gap-via-repro.snap.svg
new file mode 100644
index 0000000..a9eff61
--- /dev/null
+++ b/tests/__snapshots__/sample024-gap-via-repro.snap.svg
@@ -0,0 +1,44 @@
+
\ No newline at end of file
diff --git a/tests/sample024-gap-via-repro.test.ts b/tests/sample024-gap-via-repro.test.ts
new file mode 100644
index 0000000..adb574c
--- /dev/null
+++ b/tests/sample024-gap-via-repro.test.ts
@@ -0,0 +1,144 @@
+import { expect, test } from "bun:test"
+import {
+ getSvgFromGraphicsObject,
+ type GraphicsObject,
+ type Rect,
+ type Text,
+} from "graphics-debug"
+import { GapFillSolverPipeline } from "../lib/solvers/GapFillSolver/GapFillSolverPipeline"
+import type { CapacityMeshNode } from "../lib/types/capacity-mesh-types"
+import { overlaps } from "../lib/utils/rectdiff-geometry"
+import fixture from "../test-assets/sample024-mp1-gap-fill.json"
+
+test("show the real sample024 gap beside MP1.4 and its layer connectivity", async (): Promise => {
+ const inputNodes: CapacityMeshNode[] = structuredClone(fixture.meshNodes)
+ const solver = new GapFillSolverPipeline({
+ bounds: fixture.bounds,
+ boardVoid: fixture.boardVoid,
+ meshNodes: inputNodes,
+ maxGapFillPasses: 1,
+ })
+ solver.solve()
+ const radius = fixture.viaDiameter / 2
+ const viaRect = {
+ x: fixture.viaCenter.x - radius,
+ y: fixture.viaCenter.y - radius,
+ width: fixture.viaDiameter,
+ height: fixture.viaDiameter,
+ }
+ const gapNodes = solver
+ .getOutput()
+ .outputNodes.filter(
+ (node): boolean =>
+ node.center.x - node.width / 2 <= viaRect.x &&
+ node.center.x + node.width / 2 >= viaRect.x + viaRect.width &&
+ node.center.y - node.height / 2 <= viaRect.y &&
+ node.center.y + node.height / 2 >= viaRect.y + viaRect.height,
+ )
+ const fullStackNodes = gapNodes.filter(
+ (node): boolean => node.availableZ.length === fixture.boardVoid.layerCount,
+ )
+ expect(solver.solved).toBe(true)
+ expect(gapNodes.length).toBeGreaterThan(0)
+ expect(
+ [...new Set(gapNodes.flatMap((node): number[] => node.availableZ))].sort(),
+ ).toEqual([0, 1, 2, 3, 4, 5, 6, 7])
+ for (const node of inputNodes.filter((node): boolean =>
+ Boolean(node._containsObstacle),
+ )) {
+ expect(
+ overlaps(viaRect, {
+ x: node.center.x - node.width / 2,
+ y: node.center.y - node.height / 2,
+ width: node.width,
+ height: node.height,
+ }),
+ ).toBe(false)
+ }
+ for (const outlineRect of fixture.boardVoid.boardVoidRects) {
+ expect(overlaps(viaRect, outlineRect)).toBe(false)
+ }
+ const gap = gapNodes[0]!
+ const graphics: GraphicsObject = {
+ coordinateSystem: "cartesian",
+ rects: [
+ ...inputNodes.map(
+ (node): Rect => ({
+ center: node.center,
+ width: node.width,
+ height: node.height,
+ fill: node._containsObstacle
+ ? "rgba(255,0,0,0.2)"
+ : "rgba(0,200,200,0.1)",
+ stroke: node._containsObstacle ? "red" : "#888",
+ }),
+ ),
+ {
+ center: gap.center,
+ width: gap.width,
+ height: gap.height,
+ fill: "rgba(0,100,255,0.15)",
+ stroke: "blue",
+ },
+ ...gapNodes.map(
+ (node, index): Rect => ({
+ center: { x: 41, y: 44 - index * 0.8 },
+ width: 9,
+ height: 0.6,
+ fill: "rgba(0,200,200,0.1)",
+ stroke: "#666",
+ }),
+ ),
+ ],
+ circles: [
+ { center: fixture.viaCenter, radius, fill: "blue", stroke: "blue" },
+ ],
+ texts: [
+ {
+ x: 17.6,
+ y: 47,
+ text: "sample024 / MP1.4: gap-fill layer connectivity",
+ fontSize: 0.65,
+ anchorSide: "bottom_left",
+ },
+ {
+ x: 17.6,
+ y: 45.8,
+ text: `${fullStackNodes.length} through-via regions / ${gapNodes.length} gap nodes`,
+ fontSize: 0.55,
+ anchorSide: "bottom_left",
+ },
+ { x: 20.7, y: 42.4, text: "MP1.4 copper", fontSize: 0.45 },
+ { x: 28.8, y: 41, text: "Existing free region", fontSize: 0.45 },
+ {
+ x: 17.6,
+ y: 37.8,
+ text: "Blue 0.3 mm via fits all 8 layers",
+ fontSize: 0.45,
+ anchorSide: "bottom_left",
+ },
+ {
+ x: 36.5,
+ y: 44.8,
+ text: "Routing regions",
+ fontSize: 0.45,
+ anchorSide: "bottom_left",
+ },
+ ...gapNodes.map(
+ (node, index): Text => ({
+ x: 41,
+ y: 44 - index * 0.8,
+ text: `availableZ: ${node.availableZ.join(",")}`,
+ fontSize: 0.4,
+ }),
+ ),
+ ],
+ }
+ await expect(
+ getSvgFromGraphicsObject(graphics, {
+ backgroundColor: "white",
+ svgWidth: 1200,
+ svgHeight: 550,
+ }),
+ ).toMatchSvgSnapshot(import.meta.path)
+})