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

Spec Tooling

Status: Current Last modified: 2026-10-02 (commit 2d7e886b)

What the generator crates ARE. For the spec system’s contract, which is what you need to write or change a spec, read Spec System; for the procedure, Spec Workflow. This page covers only the tooling, so the three do not overlap.

Two crates, one workspace

spec/ is its own cargo workspace, so every command needs --manifest-path spec/Cargo.toml.

CrateOwnsDepends on the parser?
spec/tools (generators)reading specs and emitting artifactsNo
spec/runtime-toolsanything needing the live parser or modelYes

That split is the point. spec/tools reads markdown and JSON and produces tests, fixtures, docs and generated Rust; it never parses CHAT. Work that has to actually run the parser (verifying a spec example emits its codes, mining the corpus) lives in spec/runtime-tools.

The artifact registry is split along the same line, and for the same reason: generators::artifacts::ARTIFACTS holds everything derivable from markdown alone (plus, since R4, the observation snapshot as a data-file input), and the runtime half holds the artifacts that need the live parser or ErrorCode enum: the observation snapshot itself (which regenerates FIRST, being an input to the tree-sitter corpus), the DiagnosticKind registry, and the book’s artifact table. spec_gen runs both halves in dependency order, so a contributor sees one command and one list; the generated artifact table included in the spec-system chapter is the live inventory.

Layout of spec/tools

src/
  bin/          one binary per generator
  spec/         markdown spec loaders (constructs, errors)
  output/       formatters (tree-sitter corpus, Rust tests, docs)
  form_markers/ the form-marker registry: typed model, renderers, drift gate
  templates/    Tera templates wrapping fragments into whole CHAT files
  generated/    generated symbol sets (never edited by hand)

Determinism, and what enforces it

Generation must be idempotent: a re-run with no source change produces no diff. Three things make that true rather than hoped for.

  • Generators write only when content differs, so a no-op run does not churn mtimes.
  • Rust output is formatted by the generator, which runs rustfmt itself. Otherwise just fmt and the generator each rewrite the same bytes forever, both correct. Both registries do this.
  • Drift gates compare committed artifacts against what the generators produce, calling the real generators rather than a second description of their output. See Spec System for the full list.

This page last changed: 2026-10-02 (commit 2d7e886b). The whole book last changed: 2026-10-07 (commit 5e895791).