Input fingerprint
A design re-renders only when its inputs actually changed; the stamp is written in the same commit as the artifacts, so it can't claim a freshness they don't have.
Behind the scenes
Between a one-line idea and a merged, printable design sits a pipeline that gates every change, regenerates its own artifacts, and runs itself. Here is what it does under the hood — and where the generic half ends and the 3D-printing half begins.
Overview
The shape of it
print-bench is a generic pipeline with a 3D-print workshop bolted on. The whole design is drawn so those two halves come apart — which is what makes the CI half reusable as a template. They meet at exactly three seams.
Domain layer — the part you replace
The geometry, the printability gates, the co-design loop. Nothing here is generic — swap it and the same machine builds something else.
Platform layer — generic, the template
Selects its own checks, keeps derived files honest, proposes new gates, and turns issues into draft PRs on its own. It never mentions millimetres.
| Seam | Platform side | Domain side |
|---|---|---|
| Classification | a CI job asks "what runs for this diff?" | ci-classify.sh maps changed paths → gates |
| Regeneration | a job commits derived artifacts back | render.sh, gallery.sh produce them |
| Gating | the gate job runs the selected checks | gate.sh, check.sh are the gates |
Full write-up: docs/architecture/README.md.
The short version
Strip out the engineering and this is the whole story: you describe what you need, an AI helps you model it, the machine proves it will actually print, and you download a file that's ready to slice.
New here?
Bring an idea or claim a queued one, and the repo walks you from a blank directory to a merged, printable design. Every station is one command or one skill; the review round in the middle repeats until the gates pass.
The pipeline
Step through it
The same pipeline, one step at a time — from the diff you push to the single gate that lets it merge, then how the machine regenerates and runs itself. Step through with the arrows or the dots.
What a change goes through
Push a change to a model and the pipeline gates only the designs it
touches (plus anything derived from them). The check that actually decides is
gate.sh --slice: a model that renders cleanly can still fail to
be watertight, or fail to slice.
render.shRenders the STL and a 4-view contact sheet (including a bottom view, to catch overhangs and bed contact).
check.shFast pass: syntax-checks every model, CGAL-renders the library demos, and fires the guard / mate negative tests — the checks a render alone can't make.
gate.sh --sliceThe real bar. Runs the printcheck analyzer (watertight, thin walls, overhangs) on each printable part, then test-slices it in PrusaSlicer.
readme-gate.shEvery design ships a complete product page — pitch, embedded preview, print settings, tunable parameters.
style-check.shIf the design declares a style, its geometry is held to that style's measured rules.
/preflight runs this exact set,
scoped the way CI scopes it, and answers "would CI pass?" before you push.The domain layer in full: docs/architecture/design-workflow.md.
Derived files are the pipeline's job
Previews, animation GIFs, studio product shots, the gallery — anything derived from a model — are not hand-committed. CI regenerates them in the same run that gates the source and commits them back to the branch. So the presence-only gates on those images stay honest: the picture beside a model cannot depict geometry the model no longer has.
A design re-renders only when its inputs actually changed; the stamp is written in the same commit as the artifacts, so it can't claim a freshness they don't have.
The commit-back re-triggers CI, so a non-reproducible renderer could push forever. The job recognises its own last commit and refuses a second push.
CI can't push to a fork, so there it fails with the exact list of files to regenerate by hand — never a silent skip.
Committing back turns on a quirk worth stating plainly: a push made with the default token triggers no workflow. The pipeline uses that fact in both directions.
| Push | Token | Why |
|---|---|---|
| regenerated artifacts → a PR | PAT | must re-trigger CI so the required checks attach to the commit that ships |
| a newly-approved gate | PAT | same — the enabled gate has to run |
| the telemetry roll-up | default token | must not re-trigger, or it would gate, record itself, and commit forever |
The platform layer in full: docs/architecture/ci-platform.md.
It decides what to run
The changes job classifies a diff and every other job keys
off it — a docs-only change skips the render jobs, a one-design change gates
one design, an infra change gates everything. That decision lives in one
script, ci-classify.sh, that both CI and
the local pre-push check run — so "would CI pass?" locally can't drift from
what CI does.
Above it sits smart CI (tools/ci-gates):
it detects checks that don't exist yet — a shell script with no shellcheck
gate, a new top-level directory the classifier doesn't cover — and proposes
them in a sticky PR comment a maintainer crosses with one command.
ci-ok job gates the branch: it passes only when every
other job is green or was deliberately skipped. New blocking checks stay
proposals until a human opts in — nothing lands unannounced.Runs itself
It runs itself
A scheduled routine picks one opted-in issue and runs it to a draft PR for
a human to review. The reusable core is a pure, tested selector
(tools/backlog-burn) that chooses at most one
issue per firing and excludes anything already claimed by a lock, an open PR,
or a human-decision hold. Three routines share that one selector, each pointed
at a different label:
Two safety properties are load-bearing. Arming takes two keys — a committed config and a live repo switch must agree, so a clone can't silently arm it and a human can disarm it in seconds. And when an agentic run hits a yes/no question only a human should answer, it parks the issue with a label and stops, rather than guessing.
It measures itself
Each gate run writes one JSON record — printcheck scores, per-design wall
time, preview-budget headroom, what was skipped and why — and default-branch
runs append it to a committed log
(telemetry/log.ndjson →
telemetry/REPORT.md). The self-running
features read that log, so their decisions rest on measured history rather
than a guess.
One more thing
The site invents no content: every word and image traces to a committed, CI-gated file or a first-party record. That holds for this page too — its copy is a presentation of the architecture docs in the repo, and each mechanism above links to the file that implements it. A local link that doesn't resolve fails the build rather than 404ing in production.