---
title: "The Millimetre Problem: Introducing the lab-hardware-cad Agent Skill"
description: "Introducing lab-hardware-cad, an open-source Agent Skill that turns any AI agent into a CAD engineer for lab hardware, benchmarked over 98 geometry-scored runs."
updatedAt: "2026-08-15"
author: "Timothy Kassis, PhD"
authorTwitter: "timothykassis"
authorLinkedIn: "https://www.linkedin.com/in/timothykassis/"
tags: ["AI", "Skills", "Scientific Agent Skills", "Benchmarks", "Open Source", "Hardware"]
canonical: "https://www.k-dense.ai/blog/lab-hardware-cad-skill"
---
Ask a lab what their last printed part got wrong and you will not hear "the fillet radius."

You will hear that the carrier was half a millimetre tight and jams on half the plates. That the adapter was drilled on a one-inch grid when the optical table is metric, so by the fourth hole it is 1.6 mm out and will not bolt down. That the mould came back as a chip, channels cut *into* the block instead of standing proud of it, and the first cast was a flat slab.

None of those are hard geometry. Every one is an **interface**: a dimension fixed by a published standard or a vendor drawing, which costs a print cycle at best and a week at worst when it is wrong.

That is the problem our new [`lab-hardware-cad`](https://github.com/K-Dense-AI/scientific-agent-skills/tree/main/skills/lab-hardware-cad) skill is built around. It is the latest addition to K-Dense's open-source [Scientific Agent Skills](https://github.com/K-Dense-AI/scientific-agent-skills) library, and this post introduces it the way we think every skill should be introduced: with what it is, and with what happened when we tried to break it: 98 runs across two models, scored by measuring the exported geometry.

## What the skill is

`lab-hardware-cad` is not a CAD tutorial. It is a set of rails for an agent designing laboratory hardware:

- **Route the request** to one of four device families (labware fixtures, optomechanics, microfluidics, behavioral apparatus), each with its own reference file of family-specific traps.
- **Look interface dimensions up** in a bundled standards table (`standards.json`) instead of recalling them from training data.
- **Choose the fabrication process *before* the geometry**, because the clearance an FDM printer needs is not the clearance a CNC mill needs.
- **Write the design as parametric Python** ([build123d](https://build123d.readthedocs.io/)) with named parameters carrying their units, an `interfaces()` declaration naming each standard the part must satisfy, and a manifest recording the source hash and resolved parameters alongside the exported STEP.
- **Pass two gates before calling it finished**: a machine-checkable interface test, and a rendered picture the agent has to actually look at.

The output is a fabrication-ready STEP file plus a handover note a fabricator can act on. The thesis behind the rails is simple: `is_valid: true` and a correct bounding box are both fully consistent with a pocket cut on the wrong face, so validity checks are not enough; interfaces have to be designed from the standard and verified deliberately.

Does any of that actually change what the model produces? We measured it.

## How we tested it: same prompt, one difference

Two arms. Identical prompts, identical Python environment, and, within each comparison, the identical model. The only difference is whether `.claude/skills/lab-hardware-cad/` exists in the working directory. The whole grid ran twice: on Sonnet 5 (five runs per task per arm), and again on Opus 5 (two runs per task per arm) to test whether a stronger model changes the answer.

Both arms got a pre-built environment with `build123d 0.11.1` already installed, so this measures the skill and not package availability. Both were told the same thing about the deliverable. Neither was told anything about standards, tolerances, or how to check its work. The skill arm was never told to use the skill; the model decided for itself, and did, in all 49 of 49 skill-arm runs on both models. The baseline was verified clean: across all 49 baseline runs, the string `lab-hardware-cad` appears in **zero** tool calls.

### Seven tasks, each with a trap

Each task is phrased the way a scientist would ask, and each hides a specific way to be plausibly wrong.

| Task | What it asks for | The trap |
|---|---|---|
| **Microplate carrier** | A pocket a 96-well plate drops into | Sizing the pocket to the plate's nominal 127.76 mm jams on every plate built to the top of its tolerance. It must be cut to maximum material, 128.01 mm, and *then* given clearance. |
| **Cuvette holder** | Four standard cuvettes, beam at 15 mm | The same maximum-material trap at small scale, plus a holder that positions perfectly and then stands in the beam. |
| **30 mm cage plate** | Holds a 1″ filter on four ER rods | Cage geometry is fixed: Ø6 mm rods on 30.0 mm centres, aperture on the optical axis, and the aperture must be *smaller* than the filter or the filter falls through. |
| **Breadboard plate** | Bolts to a *metric* breadboard, three grid steps across | 3 × 25.0 = 75.0 mm metric; 3 × 25.4 = 76.2 mm imperial. A 1.2 mm error no bounding-box check can see. |
| **PDMS master mould** | A mould to cast a serpentine chip | **Polarity.** A master carries the channel raised. Cut it in and you have built the chip, not the tool that makes it. |
| **96-well jig** | Holes over columns 1–6 only | Masking half the plate breaks the centring shortcut: A1 must sit 14.38 mm from the left edge. Centring a 6 × 8 grid puts it at 41.38 mm and every hole misses. |
| **"Standard" Y-maze** | A mouse Y-maze "in the standard dimensions" | **There is no standard.** This scores calibration, not geometry: does the run say so, or invent plausible numbers that silently break comparison with prior work? |

### The answer key does not come from the skill

Grading a skill against its own bundled data proves nothing. So the answer key was built from primary sources, including downloading and reading [ANSI/SLAS 1-2004 (R2012)](https://www.slas.org/SLAS/assets/File/public/standards/ANSI_SLAS_1-2004_FootprintDimensions.pdf) and [ANSI/SLAS 4-2004 (R2012)](https://www.slas.org/SLAS/assets/File/public/standards/ANSI_SLAS_4-2004_WellPositions.pdf) directly. Every assertion carries the clause it came from. That also let us audit the skill's own table: against the primary documents, `standards.json` is right eight for eight on everything these tasks touch, with two corrections worth making, covered at the end.

Scoring measures the part, not the prose. A separate evaluator opens every exported STEP and measures it (pocket walls, hole centres and diameters, bore spacing, aperture position, beam clearance, and the fill profile that separates a mould from a chip) without ever learning which arm produced the file. The evaluator is itself tested against synthetic parts whose dimensions are known exactly, including deliberately **wrong** ones: an imperial bolt grid, a pocket cut to nominal, an inverted mould, a well grid centred instead of placed from A1. It passes all 69 of its checks. Only then was it allowed to grade anyone.

## The honest headline: it is closer than you would guess

On getting published dimensions right, the skill is ahead on Sonnet 5 (**34 of 35 against 30 of 35**, inside overlapping intervals), and on Opus 5 it is a dead tie at 13 of 14 each way. The differences you *can* bet on are elsewhere, they are absolute, and they hold on both models.

![Headline result: share of runs whose exported geometry meets every published interface dimension, without and with the skill, on Sonnet 5 and Opus 5](figures/fig01_headline.png)

This is worth sitting with, because it is the opposite of the marketing version. Handed a microplate carrier, Sonnet 5 with no skill at all writes 127.76 × 85.48 mm. Told the breadboard is metric, it uses 25.0 mm and not 25.4: five runs out of five, no imperial substitution anywhere. Asked for a 96-well jig masked to columns 1–6, it sets A1 at 14.38 mm from the left edge rather than centring the grid. Asked for a master mould, it puts the channel in relief. Every one of those was a deliberate trap, and the baseline walked past all of them.

Where the geometry difference does concentrate is the mould: five of five baseline runs got the polarity right, but only two of five produced a mould that was correct in every other respect. One had its casting frame floating clear of the base plate. Another swept the channel profile centred on the base plane, so half the ridge is buried in the slab and the cast channel comes out 0.15 mm deep instead of 0.30, which by the skill's own reference raises the pressure needed to drive it by about an order of magnitude.

![Fabrication-ready runs broken down by task, for both models and both arms](figures/fig03_per_task.png)

## The gap between "meets the standard" and "will actually fit"

Meeting a published dimension is not the same as fitting. A 96-well plate is 127.76 ± 0.25 mm, so plates come off the line anywhere up to 128.01 mm. A pocket cut at 127.76 satisfies the standard and jams on half the plates you own. The rule the skill enforces: size the receiving feature against the mating part's *maximum material condition*, then add the clearance your process needs: 0.40 mm per side for FDM.

That is 128.01 + 0.80 = **128.81 mm**. It is the one number in this benchmark that separates the arms completely.

| Arm | Pocket length, every run (mm) | Spread | Meets 128.81 |
|---|---|---|---|
| Sonnet 5, without | 128.36, 128.56, 128.56, 128.76, 128.76 | 0.4 mm | 0/5 |
| Sonnet 5, with | 128.81, 128.81, 128.81, 128.81, 128.81 | 0 mm | 5/5 |
| Opus 5, without | 128.76 * | n/a | 0/2 |
| Opus 5, with | 128.81, 128.81 | 0 mm | 2/2 |

Both arms clear the published 128.01 mm. But the baseline's numbers are scattered across three distinct values spanning 0.4 mm: they are *nominal plus a clearance someone picked*, which leaves as little as 0.35 mm of room over a maximum-material plate. The skill arm produced the same number, to the hundredth of a millimetre, five times out of five, arrived at from the standard rather than from a habit. (The asterisk: one unaided Opus run built a corner-tab frame the evaluator could not reduce to a single wall-to-wall span. Unmeasurable counts as unverified, and unverified counts as a fail, the same convention the skill itself applies.)

![Signed error against the published value, one dot per run, on the dimensions where at least one run missed; the pale band on each row is that dimension's allowed tolerance](figures/fig02_error_mm.png)

## Where the arms separate completely: is it an artifact or a file?

There are no confidence intervals to argue about here: on both models, nearly every one of these measures is 0% against 100%.

![Reproducibility and provenance: parametric model, regenerates on change, provenance record, declared interfaces, and rendered inspection, with the baseline near zero on everything except looking at the render](figures/fig05_process.png)

The baseline writes a script that emits a STEP. The skill arm writes a *model*: named parameters carrying their units, an `interfaces()` declaration naming each standard it must satisfy, a manifest recording the source hash and resolved parameters alongside the geometry. And, crucially, a design you can change.

We tested that last claim mechanically rather than taking it on faith: take the model the run left behind, change one named dimension, re-generate, and check the exported geometry actually moved. Baseline: **0 of 49** runs across both models. Skill arm: **49 of 49**. When your collaborator asks for the pocket 2 mm deeper, one of those is a one-line edit and the other is asking a language model to write the whole part again and hoping the rest comes back the same.

One honest caveat on the last pair of bars: every skill-arm run rendered the part and read the image back, but so did 74% of Sonnet 5's baseline runs (unprompted, with their own matplotlib) and every single Opus 5 baseline run. Looking is a habit strong models adopt on their own. What they do with what they saw is another matter:

![A baseline master mould whose casting frame floats clear of the base plate it is meant to seal against](figures/exhibit_broken_mould.png)

This master mould came from a Sonnet 5 baseline run. The casting frame is offset from the base plate: in the top view the two rectangles overhang each other on all four sides, and the frame is partly floating in mid-air. Pour PDMS into this and it runs out. The file reports `is_valid: true`, one solid, and a plausible bounding box, and the run rendered a preview, read it back, and shipped the part anyway. Looking is not the same as knowing what to look for; the skill's family references exist to supply the checklist.

## The handover note

A STEP file with no note is a part nobody can check. Scored blind against the skill's own pre-fabrication reporting checklist (by a separate model that never learns which arm wrote the note, with tool names rewritten to neutral equivalents first), the skill arm averages **5.74 of 7** against the baseline's **2.54** on Sonnet 5. The items that move are the ones that matter for trust: whether every interface dimension arrives with a source (8.6% → 71.4%), and whether the note says what the picture showed rather than merely that one was made (22.9% → 100%).

![Blinded handover-note scores against the skill's reporting checklist, item by item, for both models](figures/fig06_report.png)

## Asked for something that does not exist

The Y-maze task asks for "the standard dimensions" of an apparatus that has none. Mouse Y-maze geometry varies by lab and by paper; a confident set of plausible numbers silently breaks comparison with whatever prior work the lab is replicating, which is worse than an admitted gap.

This is the sharpest result in the benchmark. **Across seven unaided runs on two models, not one said that Y-maze dimensions are not standardised.** Every skill-arm run on both models said exactly that, and asked for the protocol being replicated. The baseline was not careless: some of its runs asked about the protocol too, and 60% of Sonnet 5's cited a real, checkable paper for the numbers they used. What no unaided run would do is challenge the question's premise. Asked for "the standard dimensions," they all supplied dimensions.

![Calibration on the Y-maze task: said no standard exists, named a checkable source, labelled assumptions, with the skill arm at 100% on all three and the baseline at zero on the first](figures/fig07_calibration.png)

That is the failure mode the skill's behavioral-apparatus reference is written to prevent, in almost these words: *"a plausible-looking wrong number is worse here than an admitted gap, because it silently breaks comparison with prior work."* On this task the rails do exactly what they claim.

## Does a stronger model close the gap on its own?

The obvious objection is that the skill is propping up a model that is not good enough, and a better one would not need it. So the whole grid was re-run on Opus 5. On geometry the objection is **correct, and completely**: 13 of 14 without the skill, 13 of 14 with it. The stronger model does not need help getting published dimensions right.

If the skill's value were dimensional knowledge, that would be the end of it. It is not.

| Opus 5, 14 runs per arm | No skill | With skill |
|---|---|---|
| Geometry meets every published dimension | 13/14 | 13/14 |
| Rendered the part and looked at it | 100% | 100% |
| Parametric model with `build()` | 7.1% | 100% |
| Regenerates when a parameter changes | 0% | 100% |
| Provenance record beside the STEP | 0% | 100% |
| Note says *what* the picture showed | 0% | 100% |
| Said plainly there is no standard Y-maze | 0% | 100% |

Two of those rows deserve to be read together. Opus 5 renders its own work and inspects it in 100% of unaided runs, better than Sonnet 5 managed and a genuine point against needing the skill for that habit. And then it does not tell you what it saw: in 0% of those runs the handover note describes what the inspection showed. The check happens; the result never reaches the person who will fabricate the part. With the skill, 100%.

That is the shape of the whole result in miniature. The frontier model is better at the engineering. It is not thereby better at *handing the engineering to someone else*, and the second of those is what the rails are actually for.

## What it costs

![Cost and wall-clock per run, and per run that produced a fabrication-ready part, for both models](figures/fig04_cost.png)

On Sonnet 5 the skill costs **1.65×** more per run ($1.47 against $0.89) and takes 408 s against 270 s. It reads a family reference, queries the standards table, generates, checks, renders, and often repairs and regenerates: the workflow doing what it says. On Opus 5 the premium shrinks to **1.13×**, because the stronger model needs fewer repair loops to satisfy the same gates.

Whether that is worth it is a question about what a failed part costs you. At laboratory scale the answer is not close: a dollar of tokens against a print cycle, a shipping wait, or a plate of cells.

## Four edges the benchmark found, and what we are fixing

Running a skill hard enough to measure it is the fastest way to find its edges. Each of these is reproducible from the benchmark repository, and we are publishing them because a skill introduction that only reports its strengths is an advertisement.

**1 · The interface gate never looks at the geometry.** This is the important one. `check.py interfaces`, the step the skill calls "the check that gates fabrication," reads the interface values a model *declared* and compares them to the standards table. It does not open the STEP. The very first pilot run showed why that matters: a model declared a correct Ø6.6 mm M6 clearance hole, passed three of three interface checks, and exported a plate whose every bolt hole is Ø11.0 mm straight through: a counterbore placed on the wrong workplane ate the whole thickness, and the screw drops clean through. The fix is small and the machinery already exists: locating bores by clustering circular edges is about forty lines, and a third column in the report (*declared / measured / standard*) turns a silent pass into a hard failure. Until then, read the skill's own sentence strictly: a passing interface check confirms the number you **wrote down** is right, not that you built it.

**2 · Mould polarity is invisible in the numbers, and nearly invisible in the picture.** We built one parametric mould with a single switch, `polarity = "mold" | "chip"`, and generated both. Identical bounding box. Identical surface area to four decimal places. Centres of mass 2.8 µm apart. Volume differs by 0.70%, and nothing in the toolchain knows what the volume should have been. One of these casts chips; the other *is* the chip. At the default render scale the 0.3 mm relief on a 12 mm part is roughly two pixels. Two fixes, either sufficient: assert polarity numerically by sectioning the solid just above the base and reading the fill fraction (a mould reads about 0.41, a chip about 0.92; the two never come close), or give the snapshot tool a zoom or section view.

![Section fill fraction cleanly separates a mould from a chip where every conventional check ties](figures/fig09_polarity.png)

**3 · The worked example in `SKILL.md` has a shallow-pocket bug.** Run verbatim, the microplate-carrier snippet passes generation, validity, and both interface checks, and produces a pocket 3.5 mm deep with an 8.5 mm floor, not the 2.5 mm floor its own parameter promises, because the subtract box is aligned from the wrong datum. It is the skill's own thesis demonstrated in its own documentation, caught by the mandatory picture and by nothing else. It matters because the example is what models copy, and several runs copied its structure.

**4 · Two small corrections to `standards.json`.** The 384- and 1536-well A1 offsets are flagged `verified: false` but are in fact stated explicitly in ANSI/SLAS 4-2004: the values are right, and the flag sends users chasing a document that already agrees with them. And the 1536-well positional tolerance is missing: §4.3.4.1 specifies Ø0.50 mm, tighter than the Ø0.70 mm governing 96 and 384, so the obvious guess is 40% too loose.

## What this means if you build lab hardware

The result that generalises is not "the skill makes the model better at CAD." If you need one part, once, and you will measure it yourself before you print it, the baseline gets you there for less money, and on the stronger model it gets you the same part. What the skill changes is **what you are left holding afterwards**.

| You end up with | Without the skill | With the skill |
|---|---|---|
| A STEP that meets every published dimension | 30/35 | 34/35 |
| Told you plainly when there was no standard to design to | 0% | 100% |
| A pocket that fits every conforming plate, not just the small ones | 0/5 | 5/5 |
| A design you can change by editing one number | 0% | 100% |
| A provenance record tying the file to the source that made it | 0% | 100% |
| A handover note a fabricator can act on | 2.54/7 | 5.74/7 |

(Sonnet 5 figures; every row except the first repeats exactly on Opus 5, where the first row is a tie.)

Those are the properties that decide whether a part is a one-off file or a piece of laboratory equipment. A design that regenerates from a named parameter survives the reviewer who wants the pocket 2 mm deeper. A manifest that records the source hash next to the geometry survives the question, eight months later, of which version of the carrier produced the figure in the paper. A note that names its sources survives the collaborator who wants to know whether 128.81 came from a standard or from a hunch.

### Three things worth stealing even if you never install the skill

1. **Size receiving features to maximum material, then add clearance.** Nominal plus a clearance is not a fit. It is the single most transferable rule here, it is the one the baseline never applied, and it costs nothing to adopt.
2. **Render the part and look at it after every regeneration.** Not once at the end. Validity and a correct bounding box are consistent with a pocket cut on the wrong face, a counterbore that swallowed the whole plate, and a casting frame floating clear of the base it is meant to seal against. All three are in this dataset, and all three reported `is_valid: true`.
3. **Make the source the artifact, not the STEP.** Named parameters with units, a declaration of what the part must fit, and a record of what produced the file. This is where the skill's advantage is total, and where it is least about CAD.

Where would we use it? For a one-off bracket you will hold in your hand tomorrow, the rails are overhead. For anything that mates with standardised labware, gets iterated, goes into a methods section, or gets handed to someone else to fabricate, the 1.65× is the cheapest part of the job.

## What this benchmark cannot tell you

Stating these plainly. **Nothing here was fabricated**: every conclusion is about exported geometry measured against published dimensions, and a part that measures correctly can still fail on the printer, in the autoclave, or in contact with a solvent. **The sample supports "clearly separated" conclusions and nothing finer**: 35 runs per arm on Sonnet 5 and 14 on Opus 5, with Wilson 95% intervals that are wide where the bars are close, and we say so rather than reading a difference into an overlap. **Seven tasks is a sample, and we chose them** to probe traps the skill's own documentation names, which is the fair way to test whether the rails work, and also a way of testing where the skill is strongest. **The report scores come from a language model**: blinded, fixed rubric, but judgements rather than measurements, and where they disagree with the geometry, the geometry wins. **One evaluator, one author**: mitigated by a test suite with known-wrong parts and by every measurement being reproducible from the committed STEP files, not substituted for an independent implementation.

Every number in this post traces to a results table in the benchmark repository and was substituted into the source article at build time rather than typed in. Total API spend across the 98 scored runs: **US$146.82**.

The skill is open source, along with the rest of the library: [github.com/K-Dense-AI/scientific-agent-skills](https://github.com/K-Dense-AI/scientific-agent-skills). Drop `lab-hardware-cad` into your agent's skills directory (`.claude/skills/` in Claude Code, or wherever your agent of choice loads Agent Skills from), ask for the part the way you would ask a colleague, and check the handover note before you print. That last step is the one the skill cannot do for you. Yet.
