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

Guardrails

Guardrails keep the code clean while many people (and agents) change it. Each one below is a check that runs automatically — locally with cargo xtask ci, and on every pull request in GitHub Actions — so the rules hold without anyone having to remember them. Most are adopted from VectorCraft; the ADR-0009 audit says which, and what we changed.

The checks

GuardrailEnforcesConfigured inRuns
FormatOne style, no bikesheddingrustfmt.tomlcargo xtask ci · CI
Clippy, warnings deniedIdiomatic, bug-prone patterns flaggedworkspace [lints], clippy.tomlcargo xtask ci · CI
No panicsNo unwrap, expect, panic!, unreachable!, todo!, unimplemented!, dbg! outside tests; indexing_slicing denied in parsersworkspace [lints.clippy], crate attributescargo xtask ci · CI
No unsafeunsafe_code denied in the workspace and forbidden at every crate root, the compatibility probe’s included, except the plug-in’s: its ABI shim alone may use unsafe, with a // SAFETY: comment of its own above each blockworkspace [lints.rust], cargo xtask unsafe-auditcargo xtask ci · CI
DeterminismNo HashMap/HashSet, RandomState, platform sin/cos/atan2/powi and their kin, mul_add, SystemTime or Instant; no files, threads, environment or processes in library crates; rand and getrandom only through proptestclippy.toml disallowed-types / disallowed-methods, deny.toml [bans]cargo xtask ci · CI
LayeringCrates depend only on lower layers (architecture); dependencies like clap or tiny-skia only where allowedxtask/src/layers.rs (append-only table)cargo xtask layers · CI
Module sizeRust files warn above 800 lines, fail above 1,500 (generated files exempt)xtask/src/filesize.rscargo xtask filesize · CI
WebAssemblyL0 to L3 crates and the plug-in build for wasm32-unknown-unknown, and stitch, built for WASI and run by wasmtime, makes every golden file of the conformance cases byte for bytextask/src/wasm.rs, xtask/src/conformance/wasm.rscargo xtask wasm · cargo xtask wasm --goldens · CI
Movable into VectorCraftEvery package is stitchcraft-*; crates inherit only workspace keys VectorCraft defines; tooling works from its own folder on StitchCraft’s packages (ADR-0011)xtask/src/layers.rs, xtask/src/util.rs, xtask/src/join.rsmove job in compat.yml · daily and on PRs touching manifests or tooling
No GPL codeNo GPL/AGPL licence text, in any case, nor a module’s short GPL notice, and no pasted Python source anywhere in the repository: every text file, documents and files without an extension included (ADR-0012)xtask/src/cleanroom.rscargo xtask cleanroom · CI
DocsGenerated pages fresh and none left over; links, anchors (as mdBook makes them) and included files valid; mentioned cargo xtask commands exist; REQ- and DEV- ids exist, and a code is registered once its milestone is done; no long sentence on two pages; every repository path named existsxtask/src/docs.rs, xtask/src/docs_audit.rs, xtask/src/markdown.rscargo xtask docs --check · CI
Design pages follow the codeEvery source file of a crate or app is on a page’s implements comment, and a branch that changes those files changes the page too or records a Design-reviewed: linextask/src/design_map.rs, .claude/settings.json (names the pages after each edit)cargo xtask docs --check · CI
Docs imagesEvery image a page shows is one a shot in docs/shots.toml writes, with alt text, and regenerating to the committed file. VectorCraft’s own pictures, which its pinned release makes, regenerate to within a tolerance of theirs, and its plug-in dialogs show a label for each parameterxtask/src/shots/, compat/vc-window/tests/shots.rscargo xtask shots --check · CI; cargo xtask shots --vectorcraft --check · shots-ui job
ConformanceRequirement/case consistency; every fixture has its row in the fixtures index; all cases pass; changed goldens need the golden-change label and a changelog linextask/src/conformance/, ci.yml, goldens.ymlcargo xtask conformance · CI
DependenciesLicence allow-list (GPL family denied), advisories, duplicates, sources, for the workspace, the fuzz targets’ and the window probe’s. An advisory accepted is listed with its reasondeny.tomlcargo xtask ci · CI (cargo-deny)
SpellingTypos in code, docs, commit-facing texttypos.tomlCI
ProseNew Markdown lines follow the writing style: the house rules and ai-tells, a style for phrasing that machine-written text overuses.vale.ini, .vale/styles/, xtask/src/prose.rs (Vale)cargo xtask prose · CI
MSRVBuilds on the declared rust-versionworkspace Cargo.tomlCI
Cross-platformTests on Linux, macOS and Windows, among them the conformance suite’s data cases, whose machine files must equal the golden files on each, and the same machine files made as WebAssembly (determinism)ci.ymlCI
API docsRustdoc builds without warnings: no broken, ambiguous or private intra-doc linkscargo xtask ci (RUSTDOCFLAGS=-D warnings)cargo xtask ci · CI
Public API reviewChanges to a library crate’s public API show in the PR diff, as a change to its public-api.txtxtask/src/api.rs (cargo-public-api, pinned)cargo xtask api --check · CI
Coverage ratchetNo crate’s line coverage drops below its floor, and floors only go up. A crate below its floor has its untested lines named on the pull request, those only another build of a function or a closure on the line runs includedconformance/coverage.toml, xtask/src/coverage.rs (cargo-llvm-cov)cargo xtask coverage · CI job
Performance budgetPlanning the reference hoop’s whole field (REQ-FILL-TAT-009) takes at most 2 % more instructions than recorded. Records only go down. A count below half the record fails too: the counted function was lost. Its time is reported against NFR-PERF-1’s 50 ms, with a warning above itconformance/perf.toml, xtask/src/perf.rs (valgrind’s callgrind), the engine’s field examplecargo xtask perf · CI job
Mutation testingTests notice changed behaviour: every mutant in the lines a pull request changes is noticed or a listed equivalent; no crate gets more mutants no test notices than recorded, and records only go down. A weekly part whose unmutated tree failed its tests, or a recorded crate with no mutant tried, fails.cargo/mutants.toml, conformance/mutation.toml, xtask/src/mutants.rsmutants.yml · changed lines every PR, in 4 parts · everything weekly
FuzzingReaders never panic, respect their caps and terminate; anything read round-trips and previewsfuzz/, stitchcraft-testkit::fuzz (conformance)bodies every PR · an hour nightly

cargo xtask ci runs everything that does not need extra tools; tools that are not installed locally are reported as skipped, while CI installs them and treats them as required. cargo xtask refuses an option it does not know with exit status 2, before anything runs, so a misspelt --check cannot run a command’s other mode. In GitHub Actions every problem a check finds is also an annotation, on the right line of the pull request’s diff when it names one, and a failed fuzz run puts the end of its output in one: the reason is visible without opening a log.

The performance budget counts instructions, because they come out the same on any machine and in any run, where milliseconds do not. Its check fails for a change that slows planning, on whichever runner it runs. The time is still measured and reported, against the budget the requirement states in milliseconds.

Coverage counts each build of a function apart and takes the build that runs the most lines. The unit tests and the integration tests use separate builds, a generic function has one per type it is used with, and a closure is a function of its own. So a line can count as untested though the lcov report counts it run. Only the other kind of test runs it, or only a closure on the line runs, as with a ? after a closure argument whose error the tests never bring. The coverage job names these lines and the function they count against. Run them from the kind of test that runs most of the function.

cargo xtask coverage runs the job locally. It needs cargo-llvm-cov and rustup’s llvm-tools. Without the latter, LLVM_COV and LLVM_PROFDATA can name another LLVM’s llvm-cov and llvm-profdata that read the toolchain’s profiles: LLVM 20’s read those of Rust 1.97.

Design rules (reviewed, partly linted)

  1. Everything user-visible is data first. A stitch type, parameter, diagnostic, profile or format is registered in its registry; UIs, docs and tests are generated from registries.
  2. Validate at the edge, trust inside. Untrusted inputs are parsed into validated types once (Mm, Region, typed params); internal functions take those types and do not re-check.
  3. Total functions. Every function returns for every input it accepts; impossible states are made unrepresentable by types rather than asserted.
  4. Bounded work. Loops over data charge a Budget; recursion has an explicit depth bound.
  5. No silent fallbacks. A degraded result always comes with a coded diagnostic.
  6. One crate, one error enum (thiserror), with messages a user can act on.
  7. Docs on every public item (missing_docs denied in library crates); module docs explain why.
  8. Tests next to code, conformance cases in conformance/, integration tests in tests/.

Contribution flow

  • One roadmap step per PR, titled with its id: M3.4: running stitch even spacing. Small PRs (target under ~400 lines of non-generated change) review faster and revert cleanly.
  • Conventional commit scopes (feat(engine):, fix(formats):, docs:) feed the changelog.
  • The PR template asks for: the step id, requirements touched, cases added, docs updated, golden changes justified, screenshots refreshed if UI changed, whether a sew-out is needed.
  • Required checks on main (branch protection): the ci and docs workflows. Merges are squash merges; main is always releasable.
  • Playbooks make common changes mechanical: new stitch type, new parameter, new format, new diagnostic, and the review checklist.
  • Agents (Claude Code and others) start at AGENTS.md, which links here; the same gates apply.