Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture

This page is the map of the code: which crate owns what, which way dependencies point, and where a new piece of code belongs. If you are about to add a file and are not sure where, the decision table at the end answers it.

Principles

  1. The engine knows nothing about hosts. It never sees SVG, VectorCraft, files or the network. Hosts adapt their documents into a Design and consume a StitchPlan.
  2. Pure core, effects at the edges. Generators are deterministic functions of their inputs. File I/O, clocks, environment variables and terminals live only in apps and xtask.
  3. One source of truth per concept. Parameters live in the registry, diagnostics in the diagnostics registry, machine profiles in profile data. Everything else (UI schemas, CLI flags, docs pages, test strategies) is generated from them.
  4. Layering is enforced, not suggested. cargo xtask layers fails the build when a crate reaches up or sideways.
  5. Small modules with one job. Files over 800 lines warn and over 1,500 fail (cargo xtask filesize); a stitch type is a module, not a branch in a big match.

Crates and layers

L3  stitchcraft-svg          stitchcraft-vectorcraft        (adapters)
      │                         │
L2  stitchcraft-engine ◀───────┘     stitchcraft-formats   stitchcraft-render
      │                                  │                     │
L1  stitchcraft-plan ◀──────────────────┴─────────────────────┘
      │
L0  stitchcraft-core ◀── stitchcraft-params

apps:  stitchcraft-cli (bin `stitch`)   stitchcraft-vc-plugin (wasm32 cdylib)
test:  stitchcraft-testkit (dev-dependency only)
tools: stitchcraft-xtask (`xtask/`, run as `cargo xtask`)
CrateLayerOwnsMust not
stitchcraft-coreL0Mm, Point, Vec2, tolerances, deterministic math (libm wrappers), SplitMix64 RNG, Budget, Diagnostic/Code/Severitydepend on any workspace crate
stitchcraft-paramsL0params!, ParamSpec, ParamSet, value parsing and validation, the registry audit (the list of declarations is stitchcraft_engine::registry)know any stitch algorithm
stitchcraft-planL1Stitch, StitchKind, ColorBlock, StitchPlan, Thread, palettes, MachineProfile, plan invariantsgenerate stitches; read/write files
stitchcraft-engineL2Design/Element/Shape, automatic digitizing, normalization, generators, plan assembly, finalizing for the machinedepend on formats, render or any adapter
stitchcraft-formatsL2readers/writers (PES/PEC, DST, …), quantization, format limitsdepend on engine (it encodes plans)
stitchcraft-renderL2CPU preview images of plans (thread look, simple look, overlays)depend on engine
stitchcraft-svgL3SVG → Design (geometry, styles, inkstitch:* attributes, commands), plan → SVGcontain stitch logic
stitchcraft-vectorcraftL3.vectorcraft → Design (commands in names included), effect records ↔ ParamSet, plug-in JSON objectscontain stitch logic
stitchcraft-testkittestfixtures, proptest strategies (from the registry), invariant assertionsbe a normal dependency
stitchcraft-cliappthe stitch command; all filesystem accesscontain engine logic
stitchcraft-vc-pluginappVectorCraft ABI v1 shim + plug-in manifestscontain engine logic; unsafe outside abi.rs
stitchcraft-xtasktoolsgates (ci, layers, docs, cleanroom, …), generators, compatibility runs; works from its own folder, on StitchCraft’s packages only (ADR-0011)ship to users

Intra-layer edges are allowed only where listed (stitchcraft-params → stitchcraft-core). The table lives in xtask/src/layers.rs and is append-only, like VectorCraft’s.

Why this split

  • plan below engine and formats lets the formats crate encode and decode plans without pulling in geometry and generators; a file converter (stitch convert a.dst b.pes) links only L0–L2 formats code.
  • params at L0 because both the engine (typed views) and the adapters (attribute mapping) need it, and because docs/UI generation must not depend on algorithms.
  • Adapters at L3 so the engine can be tested without any host, and a new host (another editor, a web service) is one crate, not a refactor.
  • Two apps because the CLI needs the filesystem and threads while the plug-in must stay inside a sandbox with no imports.

External dependencies

Dependencies are added deliberately and recorded in the crate’s README section “Dependencies”. cargo-deny enforces the licence allow-list (MIT, Apache-2.0, BSD-2/3, ISC, Zlib, Unicode) and denies GPL/AGPL/LGPL.

NeedCrateLicenceWhere
Deterministic transcendental mathlibm 0.2MITcore
Errorsthiserror 2MIT/Apacheall libraries
XML / SVG syntaxroxmltree, svgtypesMIT/Apachesvg
.vectorcraft documents: JSON, its coordinates read exactly, unpacked from gzip by a Rust backendserde, serde_json, flate2 1MIT/Apachevectorcraft
Rasterizing previews, and reading VectorCraft’s pictures to compare themtiny-skia 0.11BSD-3-Clauserender, xtask
JSON reports, and the plug-in’s JSONserde_jsonMIT/Apachecli, vc-plugin
Content hashes of bug-report bundlessha2 0.11MIT/Apachecli
CLI parsingclap 4MIT/Apachecli, xtask
Tooling’s TOML and JSONtoml, toml_edit, serde_jsonMIT/Apachextask
Testsproptest, instaMIT/Apache, Apachedev only

The engine’s geometry is its own, with no crate for curves, regions, offsets, nearest points or graphs: spike M0.7 accepted that (ADR-0005). Versions above are those Cargo.toml asks for (2026-10-17), and Cargo.lock is the truth.

Hosts: ports and adapters

The engine exposes one entry point and one input type:

pub fn plan(design: &Design, profile: &MachineProfile, budget: &Budget) -> PlanOutcome;   // since M3.8

pub struct PlanOutcome {
    pub plan: Option<StitchPlan>,   // None when nothing could be sewn
    pub diagnostics: Vec<Diagnostic>,
    // per-element groups, for previews and reports, arrive with the VectorCraft plug-in (M6)
}

The machine profile is an input because the shortest stitch a generator may sew is the machine’s, and finalizing (M3.9) fits the plan to the machine’s longest stitch and hoop.

and a per-element entry point, which plan calls for each element and live previews call for one:

pub fn generate(element: &Element, settings: &DesignSettings, profile: &MachineProfile,
                neighbours: &Neighbours, budget: &Budget) -> Generation;   // since M3.8

pub struct Generation {
    pub generated: Option<Generated>,   // the element's stitch groups; None when it is skipped
    pub diagnostics: Vec<Diagnostic>,
}

A live preview sees one object, so it has no neighbours, and it sews on the reference machine.

Adapters only translate:

AdapterFromTo
stitchcraft-svgSVG document (+ inkstitch:* attributes, command symbols)Design
stitchcraft-vectorcraft.vectorcraft JSON (+ commands in names), plug-in input objects, effect recordsDesign / Element
stitchcraft-vc-pluginABI v1 call (JSON in linear memory)generate → preview geometry JSON
stitchcraft-clifiles and flagsplan → formats/render/report

A future in-tree VectorCraft crate would be a fifth adapter calling the same two functions.

Errors and diagnostics

  • Diagnostics describe problems with the user’s design (a satin with three rails, a fill smaller than its row spacing, a design larger than the hoop). They are values, collected, coded and shown; planning continues where it can.
  • Errors (Result<_, XxxError>) describe failures to do the work (a file is truncated, a budget is exhausted, an I/O failure). Each crate has one thiserror enum; apps turn errors into diagnostics or exit codes.
  • Nothing below the apps prints, exits or panics.
  • A bug in StitchCraft is neither. It shows as a failed plan check (SC-E0009) or as a panic despite the lints. The command line catches it and ends with exit status 4. For stitch plan it also writes a bug-report bundle that reproduces the bug (determinism). A VectorCraft exporter declines with it, so VectorCraft doesn’t write the file.
  • The command line’s own promises. A command refuses outputs that name the same file as each other, or as a file it reads (REQ-CLI-007), and writes each file under a temporary name of its own, created new and synced before it is renamed into place (REQ-CLI-006). Built for WASI, as cargo xtask wasm --goldens runs it, a program has no process id, so there the names count from 0. What it prints of a file it wrote is the file read back (REQ-CLI-008, stitchcraft_formats::read_back, which the VectorCraft exporters call too). A closed standard output keeps a command’s exit status, any other error writing it ends with 3 (REQ-CLI-009), and help and errors are coloured only on a terminal (REQ-CLI-010).

Feature flags

Kept minimal: stitchcraft-engine/experimental exposes generators whose status is experimental in the registry (they appear in docs with a badge); stitchcraft-formats/<format> flags exist only if a format pulls a heavy dependency (none planned). No flag may change the output of a stable generator.

WebAssembly constraints

Everything in layers L0 to L3 must build for wasm32-unknown-unknown, which cargo xtask wasm checks. That target has no threads, file system, clocks, environment, processes or randomness from the OS, and std’s calls for them build there but fail or panic when run, so clippy.toml keeps library crates from calling them. The plug-in is built in release with the workspace’s profile (thin LTO, one codegen unit), and on wasm32-unknown-unknown a panic aborts. That is one more reason the no-panic rule is absolute: the module stops, and VectorCraft reports the plug-in as failed.

Where does my code go?

You are adding…Put it inAlso touch
A new stitch typecrates/stitchcraft-engine/src/generators/<name>/registry params, diagnostics, conformance cases, docs page — see the playbook
A parameterthe generator’s params! blocknothing else by hand: docs and schemas regenerate; a new …Params declaration also gets a line in stitchcraft_engine::registry::PARAMETERS — see the playbook
A machine formatcrates/stitchcraft-formats/src/<format>/format playbook
A machine profilecrates/stitchcraft-plan/src/profiles/ (data)machine-testing record
A diagnosticthe diagnostics registry in stitchcraft-coreexplanation text + a case that triggers it
SVG/Ink/Stitch parsingstitchcraft-svgcompatibility contract row
VectorCraft document readingstitchcraft-vectorcraftcompatibility gate scenario
A CLI subcommandapps/stitchcraft-cli/src/commands/trycmd example in the docs
A repo checkxtask/src/cargo xtask ci step list and the guardrails page