Metadata-Version: 2.4
Name: cadgen
Version: 0.5.0
Summary: STEP-first CAD artifact generation runtime: build123d STEP/GLB/topology generation, validation, and inspection for CAD agent skills.
Author: earthtojake
License-Expression: MIT
Project-URL: Homepage, https://github.com/earthtojake/text-to-cad
Project-URL: Repository, https://github.com/earthtojake/text-to-cad
Project-URL: Issues, https://github.com/earthtojake/text-to-cad/issues
Keywords: cad,step,glb,build123d,parametric,agent-skills
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Manufacturing
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: build123d
Requires-Dist: cadquery-ocp
Requires-Dist: ezdxf
Requires-Dist: shapely
Provides-Extra: snapshot
Requires-Dist: playwright; extra == "snapshot"
Dynamic: license-file

# cadgen

The published distribution: everything that turns CAD source into documents,
documents into derived state, and derived state into pixels and meshes. One
PyPI package carrying both language halves — the Python engine under
`src/cadgen/`, and the built JavaScript it executes under
`src/cadgen/_runtime/` (the cadgen-js runtime and the CAD Viewer's client,
bundled in at build time; the JS *source* lives in its own packages and never
ships as source).

**PURPOSE** — the engine and its command surface: model execution, the
store, document assembly, kinematics, exports, validation, inspection,
snapshots, the warm daemon and its build pool, and the CAD Viewer
(`cadgen viewer`: a local HTTP server over the built client, one directory per
instance).

**MAY DEPEND ON** — the Python ecosystem it declares (OCP/build123d lazily,
never at namespace-import time) and the *built outputs* of `cadgen-js`.
Never app code, never `cadgen-js` source at runtime.

**DEPENDED ON BY** — every skill (as a pinned installed distribution). The
CAD Viewer is not a dependent but a part: `cadgen.viewer` serves the client and
submits a document's compile as a job to the same build pool every door uses.

## The design laws

These are LAWS, not conventions: a change that violates them is wrong even
when it works. Each carries a pressure-test to apply before writing code.

### 1. Generated files are totally independent of their source code

A generated file (STEP, DXF, STL, GLB, 3MF) and its sidecar
(`<name>.step.json`) stand alone, forever.

*Pressure-test*: a generated file must be fully renderable — viewer,
snapshot, inspect — by reading ONLY the generated file(s), the sidecar, and
the store's artifact side. Never the source. A file whose bytes have no tree
in the store is compiled from those bytes (`cadgen step compile` semantics),
never from source. Deleting every `.py` in a project must not change what
renders.

- Nothing a renderer reads references the source tree: the sidecar's
  kinematics are resolved numbers and labels, its animation is COPIED
  module text; a tree and its components carry no path, script or record key
  ([`STORE.md`](STORE.md) §2, the two-sides law).
- A door never refuses a document and never auto-rebuilds: whether a
  document is behind its script is the model's record's question, answered
  by `cadgen store why` and the build tree, never by a render path.
- Source scripts are PROGRAMS: run, never passed to CLIs, never parsed by
  renderers.

### 2. The store contains only derived results

`~/.cache/cadgen` is the store: content-addressed objects (a model's result
tree and the components it is made of) and input-addressed index entries
(the per-model record, the document → tree map, op-memo and tessellation
entries) — data derivable from sources and documents, and nothing else. Its
layout, formats, gate, two-sides law and invariants are the contract in
[`STORE.md`](STORE.md); read it before touching anything that writes to or
reads from the store. Where this document and `STORE.md` disagree, `STORE.md`
is right and this one is stale.

*Pressure-test*: everything in the store is (a) a pure function of some
source or document, (b) safely deletable at any time, and (c) rebuildable
by running the models again. If losing a store entry would lose information,
that information is in the wrong place.

**The store is what the sources imply; the sidecar is what the author meant.**

There is no automatic GC: `cadgen store gc` is the only sweeper, and every
object is immutable and idempotently written, so deletion never needs
coordination — a racing reader re-misses and rebuilds. There are no locks:
atomic writes, pins and the publish rule (`STORE.md` §5, §7) decide every
concurrent outcome, and no reader ever waits on a build.

### 3. One sidecar per artifact, and it belongs to that artifact alone

`part.step` gets `part.step.json` — schema-versioned sections (kinematics,
animation). New capability = new section + schema bump, never a second sidecar
file. Model-side, beside the artifact, so it travels with the file it
describes — and it exists only when law 17 says it must.

A sidecar describes the model that declared it — never its parent, never its
children. A parent composing a child receives GEOMETRY (tree, labels, colors,
placements, exact shape) and nothing else: the child's kinematics and
animation are written by the child's own build into the child's own sidecar,
and an assembly that needs a relation declares it on the assembly. This is
what lets a cached child stand in for its function: the cache carries
geometry, and geometry is all a parent may read.
*Pressure-test*: build a child that declares `kinematics=`, then build a parent
that composes it. The parent's sidecar must contain only the parent's own
declarations, and the child's sidecar must be unchanged by the parent's build.

### 4. Zero metadata in written artifacts

A STEP or DXF is pure geometry. Provenance, kinematics, and context ride
the sidecar; the artifact separated from everything else is a plain
importable file.

### 5. Byte determinism

Same inputs, same bytes, every format — STEP (canonicalized NAUO ids and
presentation-style ordering), meshes (one deterministic tessellator), DXF
(geometry-ordered emitter). Content-addressing and every freshness ledger
depend on it.

### 6. One surface, three faces

The DECORATOR declares a capability on a model, the PUBLIC FUNCTION
(`cadgen.<format>.<verb>`) performs it, and the CLI (`cadgen <format>
<verb>`) is GENERATED from the function's signature
(`_internal/cli_from_function.py`) — never hand-written, structurally
sync-tested.

*Pressure-test*: for any option, "what is this called on the other two
surfaces?" must answer with the same name and a role-determined payload —
`kinematics` everywhere: on DECLARING surfaces (decorators, `step build`)
it is the space (`{mates, couplings, poses, at}`); on CONSUMING surfaces
(snapshot, mesh `build`) it is a point in that space (a preset name or
`{dof: value}`). One name, one validator, no synonyms.

### 7. Documents-only CLIs; scripts are programs

`python model.py` is the one source door: it gates, builds, writes every
output the decorators declare (`.step`, meshes, sidecar — STEP is one output
kind, not a required one), and rewrites declared exports that drifted. Every
CLI takes documents, resolves them by their bytes, and compiles a missing
tree from those bytes as a job in the build pool — never from a script.

A script is a program, and cadgen runs it wherever it lives. The import path
inside a build is exactly `python script.py`'s — the script's own folder, then
the caller's `PYTHONPATH` — and nothing cadgen adds or infers from directory
names. Project layout (`src/`, `lib/`, format folders) is a convention of the
skills, never a fact cadgen knows; a project that wants an import root beyond
the script's folder declares it the standard Python way (`PYTHONPATH=src`).
*Pressure-test*: move a project's folders around and rebuild; cadgen must not
care, only the project's imports may.

### 8. No backwards compatibility

Hard cutovers only. Every retired surface fails loudly with a teaching
error naming its replacement — never an alias, never a shim.

### 9. Closed vocabularies

Every declaration surface has a closed key/kind set. Unknown keys are
teaching errors, never silently ignored.

### 10. Loud failure or correct output, nothing between

The cardinal sin is plausible-wrong output at exit 0. No silent fallbacks,
no globs, no guessing; a failed render leaves NO file at the requested
path.

### 11–14. Runtime laws (shared with cadgen-js)

Kinematics is pure data and choreography is pure JS, fully independent
(11). Clients render from file + sidecar + the store's artifact side and never
read source, a record, or trigger builds (12). Correctness never depends on a
store hit (13). Composition: importing binds, calling links — a parent
depends on a child by its RESULT (the pinned tree), on a constant by its
VALUE, on a helper by its FILE — and a model must never `read_step` its own
output (14). The bundled runtime under `_runtime/` is the JS half of these;
the laws' JS statements live with the cadgen-js source.

### 15. The package ships alone

The installed distribution is the whole world: the Python engine, the
bundled `_runtime/`, and this document. It works with the repository it
was built from gone — and its markdown must read that way, referring to
nothing outside the package.

*Pressure-test*: every sentence in the package's markdown must be true and
actionable for someone who only ran `pip install cadgen`. Naming a bundled
thing ("the cadgen-js runtime bundled at build time") passes; a repo path
to its source, a repo script, or a repo workflow does not.

### 16. Decorator inputs never change the geometry

A `@step`/`@dxf`/`@stl`/`@glb`/`@threemf` decorator's arguments never change
the geometry a model produces. They decide where the files land (`out=`),
how they are written (the mesh tolerances), and what the sidecar declares
(`kinematics=`). The geometry is the function's return value and nothing
else: a `Compound` placing children is packaged as occurrences, a single
solid as one component, and `part`/`assembly` is read off the resulting tree.
A posed or differently configured export is authored geometry, or another
model.

Two features were deleted for violating this: the kinematics bake point
(`kinematics={..., "at": pose}`), which transformed the tree through its mates
before writing it, and `kind="part"|"assembly"`, whose only effect was to steer
whether the build packaged the return as one component or as occurrences.
*Pressure-test*: strip every argument off a model's decorators and rebuild;
the tree hash must not change.

### 17. A sidecar only when strictly necessary

Never write a JSON sidecar unless something beside the artifact has to read
it. Today the only legitimate content is kinematics declared by the model;
a model that declares none writes no sidecar, and a rebuild of a model that
dropped its declaration deletes the stale file. Metadata with no reader
beside the artifact — what a model declares about its own outputs, where a
build came from, when it ran — belongs in the store record, never in a
file next to the geometry.

Two sections were deleted for violating this: `meshExports`, a copy of the
mesh decorators' declarations that only a door read back (a door now
tessellates the document's tree and writes the file it was asked for), and
the animation text copied from the `.anim.js` module (animation is a render
module beside the STEP, read live by the viewer).
*Pressure-test*: build a part that declares meshes and no kinematics; no
`.step.json` may appear beside it.

## The shape of the package

```
src/cadgen/
  <format>.py            # public namespaces: step, stl, threemf, glb, dxf,
                         #   urdf, srdf, sdf — each binds its verbs
  authoring.py           # @step/@dxf/@stl/@glb/@threemf decorators; a call
                         #   builds at top level and composes (a lazy child)
                         #   inside a body; a model's outputs are what they
                         #   declare — a mesh decorator alone is a model that
                         #   writes no STEP
  kinematics.py          # typed mates vocabulary (revolute/slider/
                         #   cylindrical/fastened, couple, normalize)
  step_scene.py          # read_step and scene loading (recorded inputs)
  assembly.py            # AssemblyHelper — positioning through native joints, labels
  results.py             # the typed Results every verb returns (stdlib-only)
  store/                 # the store (STORE.md): objects, index, records, trees,
                         #   closure, gate, materialize, publish, lazy, gc, view
  cli/                   # generated command shells, one per <format> <verb>
  cli_tree.py            # the build tree on stderr / JSONL events
  daemon/                # the build pool: executors (daemon + transient),
                         #   broker (job slots, coalescing), pool (workers,
                         #   spares, extras), jobs (the ledger), server,
                         #   worker, client, transport
  _internal/             # the engine: generation pipeline, tree builder,
                         #   FK (kinematics_fk/resolve), mesh_export ledger,
                         #   cli_from_function, doors (documents by bytes),
                         #   source_sidecar, step_assemble/step_reemit
  viewer/                # the CAD Viewer's server: launcher (main),
                         #   routes (http_app), catalog (scanner), status
                         #   (artifact_status: not compiled / compiling /
                         #   rendered / failed), build_progress (the daemon's
                         #   job ledger, read over its socket)
  _runtime/              # BUILT JS (browser snapshot renderer, node
                         #   builders, the viewer client) — generated, never
                         #   edited here
```

Verbs by format: `step` compile · build · snapshot · inspect;
`stl`/`3mf`/`glb` build · snapshot; `dxf` snapshot; `urdf`/`sdf`
validate · snapshot; `srdf` validate. `cadgen snapshot` routes any suffix.
`cadgen store|daemon|doctor` are status commands, and `cadgen viewer
[list|stop]` the CAD Viewer's launcher and instance manager — all deliberately
outside the mirror pattern. `cadgen step compile` is internal tooling: skills never
teach it — doors compile a document's missing tree on demand.

Developed in [earthtojake/text-to-cad](https://github.com/earthtojake/text-to-cad);
that repo's contributor guide carries the development workflow (tests,
bundling, versioning).
