text-to-cad: The Validation Harness Behind the Agent-Designed Robot Arm

Back to blog
Mehran Mozaffari·· Updated 31 August 2026

What text-to-cad actually is: a CAD department as a skill library

The demo that made the rounds showed a 7-degree-of-freedom robot arm designed start to finish inside a coding agent — links, joints, kinematics — with no CAD application open at any point. The framing implied the model had learned to emit CAD. It hadn't, and the repository behind the demo is more interesting than the framing. text-to-cad, maintained by earthtojake, describes itself as "a library of agent skills for generating, inspecting, sourcing, slicing, and handing off CAD and robot-description artifacts from local project files." It is MIT-licensed, targets Python 3.11+, runs entirely locally with no backend, and had gathered roughly 14,000 stars and 1,500 forks at the time I read the repo.

The library ships twelve skills: CAD (STEP-first part and assembly generation), CAD Viewer (local browser previews), step.parts (search for off-the-shelf STEP parts like screws, bearings, motors, and connectors), DXF (2D drawings for cutting), URDF (robot structure files), SRDF (MoveIt2 planning semantics), SDF (simulator models and worlds), SendCutSend (upload preflight for the sheet-cutting service), DfAM Check (printability measurement), G-code (slicing through real slicer CLIs), Bambu Labs (dry-run, upload, and cautiously start local print jobs), and Implicit CAD (GLSL signed-distance-field modeling, flagged experimental).

The origin matters for judging what it is. The author — softservo on Hacker News, the same person as the earthtojake handle — says he spent ten years in software, got humbled by modern CAD tools like Onshape while brushing up on robotics, and built the harness to generate models for a 7-DOF arm he was designing. He reports it working much better than expected on recent frontier models, and states plainly that he has no intention of making a business of it. That pedigree shows in the design instincts: this is a tool built by someone who wanted his own robot arm, not a wrapper chasing a demo.

Installation is one command — npx skills add earthtojake/text-to-cad — with provider-native plugin paths for Codex, Claude Code, and Grok Build. Two operational details from the README are worth internalizing because they reveal the maintenance posture. First, npx skills add is both install and update: npx skills update only walks your lockfile and silently misses newly added skills, which matters because releases do add skills. Second, the Codex plugin path requires Codex 0.142.0 or newer, and older versions skip the plugin silently — it never appears in the plugin list. Quiet failure modes are a theme in this ecosystem, and the README deals with them head-on rather than pretending they don't exist.

Now the misconception correction, because it changes how you should read every agent-CAD demo including this one. The model never writes STEP text. The CAD skill's pinned dependencies are cadgen==0.4.28 and playwright — cadgen being the package that wraps build123d, a Python parametric modeling framework built on the OpenCascade geometric kernel. The agent writes build123d Python; OpenCascade does the boundary-representation math; the STEP file falls out of a real kernel. That is the correct architecture, and it means "the agent generated a STEP file" really means "the agent generated provable Python against a 30-year-old industrial geometry engine." Everything else in this article follows from that distinction.

The CAD skill runs a required workflow, not a one-shot prompt

Read skills/cad/SKILL.md and the first thing you notice is that generation is the easy part. The skill defines a ten-step required workflow, and only two of the steps produce geometry. The rest are classification, brief-writing, parts lookup, validation, snapshot review, and repair.

The shape of a job looks like this. The agent classifies the task (new part, new assembly, source modification, direct STEP inspection, measurement check, snapshot review, or secondary export). It writes a natural-language CAD brief capturing dimensions, units, coordinate convention, feature intent, output paths, assumptions, and validation targets. If the assembly names purchasable components — servos, motors, connectors — the workflow requires a step.parts search before it is allowed to model simplified placeholder geometry, and a miss must be recorded before falling back to a documented envelope. Only then does it author build123d Python.

The generator contract is strict: a buildable entry function named gen_step() in a file named <name>.step.py, with helper modules kept as plain .py. STEP and its generator live side by side with the same basename. The tooling around it is a small CLI family — scripts/gen, scripts/export, scripts/inspect, scripts/snapshot, scripts/artifact — with machine-parseable conventions throughout: results on stdout, progress and failures on stderr, so 2>/dev/null leaves something parseable and >/dev/null leaves a readable log. Concurrent builds of the same model wait on a lock rather than racing, and each target reports one of four outcomes: built, current, skipped-peer, or contended. Someone has clearly been burned by two agents racing the same file.

The validation layer is where the skill earns its keep. scripts/inspect refs with --facts --planes --positioning is the mandated baseline; targeted measure, align, frame, and diff checks verify what the spec actually calls out. There is an honesty note in the skill that I want to quote the spirit of: the ok field on a refs check covers reference resolution only — an open shell and an inverted solid both pass it. So scripts/inspect validate exists as a separate geometry-soundness gate. The skill knows exactly which checks prove what, and says so.

Then the step that would be first to get cut by a lazy implementer, and which the skill makes non-negotiable: snapshot review. After creating or visibly updating a STEP part or assembly, the agent must render PNG/GIF snapshots and actually review them, because deterministic checks passing is not proof the part looks right. The only documented skip cases are no visible geometry change or no valid artifact existing, and the reason must be reported. Finally, handoff: any workflow that touches .step, .stp, .stl, .3mf, or .glb must hand the file paths to the cad-viewer skill for a live preview link, or report why it couldn't.

The modeling defaults are opinionated in the right places: millimeters, XY base plane, positive-Z up, closed positive-volume solids, 2.0–3.0 mm walls for small plastic enclosures, 1.0–3.0 mm cosmetic fillets, and standard clearance holes of 3.4/4.5/5.5 mm for M3/M4/M5 fasteners. Those numbers are exactly the kind of tribal knowledge a software engineer re-entering robotics lacks, and encoding them as defaults is half the value of the skill.

Topology sidecars and selector refs: how an agent gets eyes on geometry

The deepest technical idea in the repo is one sentence from the author: the harness "generates a topology sidecar for every STEP file that can be used to quickly read the BREP (faces/edges/vertexes) without loading in the full STEP."

This is the mechanism that makes text-driven CAD iterable at all. A STEP file is a boundary representation — a graph of faces, edges, and vertices — and industrial STEP files get large fast. A 600-occurrence assembly is unreadable as text and slow to re-import wholesale on every iteration. The sidecar inverts that: the agent queries a compact index of the geometry, addresses individual entities with selector references like #o1.2.f1 (object 1, solid 2, face 1), and asks for measurements, alignment checks, or diffs between specific named features. The inspect tool answers in JSON on stdout, output volume does not grow with model size — a 600-part assembly logs the same dozen lines a single part does — and --verbose adds stage timing on stderr when needed.

The failure reporting deserves its own note because it is aimed at an agent audience. When a generator throws, the error prints the exception and the frames in your generator, not the runtime's:

[scripts/gen] FAILED: ValueError: bad radius
[scripts/gen]   models/step/parts/widget.step.py:9 in gen_step
[scripts/gen]       return _profile(radius)
[scripts/gen] re-run with --verbose for the full traceback

That is the difference between an agent that can self-repair in one turn and one that drowns in a kernel stack trace.

The author is candid about why this scaffolding exists: the planning structure in SKILL.md is "mostly a stop gap while the models don't have amazing spatial reasoning." I'd put it stronger: spatial reasoning is the one capability coding models reliably lack, precisely because their training corpus is text and geometry is not. The sidecar-plus-selectors pattern converts a spatial problem ("is this face where I think it is?") into a lookup problem ("what does #o1.2.f1 measure?"), which is the conversion that makes the whole workflow tractable. Render feedback closes the loop — snapshots and the CAD Viewer give the agent something like eyes — but the deterministic topology query is the load-bearing part, because a render can be misread by a vision model in ways a dimensioned measurement cannot.

From STEP to MoveIt: the robot-description chain is three validators deep

The arm demo is where the skill library stops being a CAD toy and becomes a robotics pipeline. The chain from solid geometry to a planning-ready robot runs through three description formats, each with its own skill and its own validator, and the split between them is a lesson in itself.

The URDF skill owns physical structure: links, joints, limits, inertials, meshes. Its core rules read like a list of past regressions. The .urdf XML is the source of truth — there is deliberately no gen_urdf() generation contract, unlike the CAD skill. Before editing, the agent must establish a design ledger (frames, joints, geometry, units, assumptions) and embed it as a comment block in the file itself. Never freehand numeric values that are the result of computation — inertia tensors, centers of mass, unit conversions — compute them with closed-form formulas or a throwaway helper script. And validation is explicitly a guardrail, not spatial proof: the skill warns that a URDF can pass every structural check while placing a joint in the wrong spot, which is why the verification recipe adds a viewer review sweeping every joint after scripts/validate passes clean.

The SRDF skill layers MoveIt2 planning semantics on top: planning groups, end effectors, group states, disabled-collision pairs. Its hard rules are unusually specific because SRDF failures are semantic, not structural. The SRDF pairs with its URDF by colocation — same folder, same <robot name> — and that is the only linking mechanism; exactly one URDF per robot name may exist in the folder. Before writing any SRDF, the agent must extract the URDF's link/joint table and copy names from it, never type them from memory. Group states must use URDF-native units (radians for revolute joints, meters for prismatic — the skill explicitly forbids storing degrees). Disabled-collision pairs require truthful, evidenced reasons: adjacency derived from the joint table, sampling from MoveIt Setup Assistant, or explicit user data. No invented blanket disables.

The SDF skill covers simulator models and worlds — frames, physics, sensors, lights. And the format-boundary section of the SRDF skill is worth pinning above any robot project: URDF owns structure, SRDF owns planning semantics, SDF owns simulation semantics, and putting geometry or joint origins in the wrong file is a classic way to build a robot that validates everywhere and works nowhere.

flowchart TD
    A[Natural-language spec + reference images] --> B[CAD skill: brief + step.parts lookup]
    B --> C[build123d generator name.step.py]
    C --> D[scripts/gen via cadgen 0.4.28]
    D --> E[STEP solid or AssemblyHelper compound]
    E --> F{inspect refs + inspect validate}
    F -->|finding| C
    F -->|clean| G[snapshot review mandatory]
    G -->|visual defect| C
    G -->|pass| H[Link meshes exported per-link frame]
    H --> I[URDF skill: XML + design ledger + computed inertials]
    I --> J{urdf scripts/validate}
    J -->|finding| I
    J -->|clean| K[SRDF skill: groups from extracted URDF table]
    K --> L{srdf scripts/validate vs paired URDF}
    L -->|finding| K
    L -->|clean| M[MoveIt2 planning review via cad-viewer moveit2_server]
    M --> N[Simulated world via SDF skill]

Three things make this chain credible to me. First, every arrow that could loop back does — validation failures return to the source that caused them, not forward. Second, mesh preparation happens before URDF authoring, one mesh per link exported in that link's own frame, because the URDF skill knows mesh-frame mismatches are the classic silent killer. Third, the chain's validators are standard-library-only Python scripts, so they run anywhere the agent runs, no ROS installation required to catch a wrong joint limit or a mimicked joint set as a fixed one.

The fabrication tail ends at a real printer, guarded by dry-runs

Most agent-CAD projects stop at a mesh file. text-to-cad follows the artifact to the physical world, and the fabrication chain encodes a gate at every transition — the same discipline the validation harness applies to geometry.

The DXF skill produces 2D drawings — profiles, templates, gaskets, cut layouts — either from Python sources or projected from CAD geometry. The SendCutSend skill checks DXF and STEP files against the real constraints of the cutting service before upload, which turns a category of rejected-order emails into a local preflight. The DfAM Check skill measures mesh printability per process: wall thickness, overhangs, support volume, and build orientation. The G-code skill slices supported meshes into printer-profiled FDM code using real slicer CLIs — not a reimplementation of slicing, the actual tooling. Then the Bambu Labs skill does the cautious thing at the end of the pipeline: dry-run, upload, and only then, deliberately, start a local print job.

stateDiagram-v2
    [*] --> Draft: generator authored
    Draft --> Built: scripts/gen outcome built
    Draft --> Current: package already up to date
    Built --> RefChecked: inspect refs --facts
    RefChecked --> GeometryValidated: inspect validate clean
    RefChecked --> Draft: open shell or inverted solid
    GeometryValidated --> SnapshotReviewed: snapshot pass
    GeometryValidated --> Draft: visual defect
    SnapshotReviewed --> MeshExported: scripts/export STL or 3MF
    SnapshotReviewed --> RobotDescription: URDF and SRDF authored
    MeshExported --> Sliced: gcode skill via slicer CLI
    Sliced --> DryRun: bambu-labs dry-run
    DryRun --> Printing: upload and cautious start
    DryRun --> Sliced: profile mismatch
    Printing --> [*]
    RobotDescription --> Planning: MoveIt2 smoke test
    Planning --> [*]

Read that state machine as an answer to the question the original demo raised — what separates a lab rig from a field-reliable artifact. The repo's answer is not a claim; it is a sequence of gates, each one cheap, local, and mandatory. DfAM Check will tell you before printing that a wall is too thin for the process or that the build orientation buries you in support material. The slicer gate catches geometry that survived the kernel but not the nozzle. The dry-run catches printer-profile mismatches without wasting filament. None of this closes the loop on load testing, wear, or control tuning — a printed arm still needs real actuators and real cycles — but it moves every checkable failure earlier, which is the only direction reliability moves in.

What the benchmarks honestly show about the limits

The repo added a small benchmark suite — versioned prompts and results under the repo's assets, starting with parts like a rectangular calibration block — and the author frames it as a start at measuring performance over time, not a leaderboard. Treat that framing as accurate, because the launch discussion surfaced failure cases the benchmarks did not catch, and they are instructive.

The sharpest observations came from people who actually ran it. One commenter noted that in an early benchmark the positions of four holes were not specified in the prompt at all — and indeed the gussets in one benchmark overlapped its holes, and a through-hole in another did not actually go through. Another user found the agent's notion of orientation disagreed with the mesh preview — a bottle holder rendered lying on its side — which the author later fixed. And a robotics-fluent commenter delivered the terse verdict that the demo project did not seem to understand how servo motors work; the author's response was a one-character frown.

Two structural criticisms deserve more than a nod. First, the language-barrier argument: one commenter reconstructed the full text prompt for a finned engine cylinder — eleven dense lines of dimensions — and showed the description still fell short, because there was no clearance between the flange and the fins to install nuts. A drawing communicates that wordlessly; prose has to anticipate it. The skill's answer is implicit in its design: the CAD brief step forces assumptions to be explicit, the defaults encode tribal knowledge (those M3/M4/M5 clearances exist precisely so nobody hand-waves a hole diameter), and the snapshot review catches what prose forgot. It mitigates the problem without dissolving it.

Second, the professional-grade argument: enterprise CAD pain is about revision control across teams, tolerance stacks, and liability, and a local skill library does not address any of that. The skill itself draws the boundary in its own text — it explicitly refuses to be used for engineering certification or FEA conclusions. I respect a tool that writes its own limits into its own docs.

What the evidence supports, then, is narrower than the demo's vibe but genuinely useful: for simple-to-moderate mechanical parts and robot descriptions, in the hands of someone who can check the output, this workflow produces real, source-controlled geometry and catches most of its own mistakes. The failure cases concentrate exactly where you'd predict — under-specified prompts, spatial relationships between features, and domain physics like servo behavior — and the harness's answer to each is another checkable gate, not more model capability.

How it compares to the other ways agents do CAD

The launch discussion doubled as a census of this space, and the differences are about where validation lives. Here is the comparison I wish I'd had before forming an opinion:

Approach Geometry engine Primary artifact Where validation lives Realistic fit
text-to-cad skills build123d on OpenCascade, via pinned cadgen STEP-first, then URDF/SRDF/SDF/DXF/G-code Deterministic inspectors, mandatory snapshot review, per-format validators Software-fluent builders who want source-controlled geometry and robot descriptions
OpenSCAD screenshot loops OpenSCAD CSG STL plus renders Human eyeballing rendered PNGs each turn Quick parametric prints; breaks down on B-rep complexity
FreeCAD scripting via agent FreeCAD's OpenCascade binding FreeCAD documents, STEP Ad hoc, human-driven Existing FreeCAD users automating what they already do
Zoo (commercial text-to-CAD) Hosted proprietary platform Vendor-managed formats Vendor pipeline Teams buying the capability as a service
Agent writing STEP text directly None A text file pretending to be STEP None Nobody — this is what the demo framing implied and what does not work

The last row is the one to internalize. The original demo read as "model emits CAD." The repo proves the opposite thesis: the model emits Python against a real kernel, and every layer of value around it is validation. The OpenSCAD-loop projects (and there were several in the discussion, from screenshot-iteration harnesses to full container workflows) share the same insight but bottom out at STL and screenshots — no B-rep, no STEP, no robot descriptions, no fabrication chain. Zoo went further toward making text-to-CAD a product; text-to-cad went wider toward making it a workflow.

Concretely, what I'd build with this now: an enclosure-and-brackets project where every revision lives as a .step.py diff I can review like code; a sensor-mount arm where the URDF/SRDF chain hands MoveIt2 a planning group that actually reflects the printed part; a sheet-metal adapter drawn to DXF, preflighted for SendCutSend, and cut without a single round-trip rejection. And what I would not build with it: anything certification-bound, anything where a tolerance stack has a dollar value attached, or anything I cannot describe with numbers — because the tool amplifies specification ability, it does not substitute for it.

Who should install it: software engineers re-entering robotics or hardware, hobbyist fabricators who already think in repositories, and teams that want agent-generated geometry under real version control with real validation gates. Who should wait: anyone hoping to skip learning what a drawing is for. The vocabulary problem is real, the benchmarks are young, and the spatial-reasoning gap in the models is the harness's whole reason to exist. But that is exactly what makes this repo worth studying even if you never print a part from it — it is a worked example of wrapping an unreliable planner in enough cheap, local, deterministic checks that its output becomes something you can ship.

Resources

Updated 2026-06-08 by Mehran Mozaffari.

Related posts