The .loom/ artifact directory
Every ddd generate system run emits, alongside the per-deployable
project trees, a .loom/ directory at the output root containing
derived artefacts — JSON / Markdown / Mermaid / C4 files the
generator produces for tooling and review, not for runtime
consumption.
<outdir>/
├── docker-compose.yml
├── docker-compose.obs.yml # opt-in observability overlay (see docs/observability.md)
├── monitoring/prometheus.yml
├── db-init/
├── <deployable-1>/
├── <deployable-2>/
├── ...
└── .loom/
├── manifest.json # what this run emitted — the next run prunes against it
├── wire-spec.json
├── messages.en.json
├── domain.mmd
├── workflows.mmd
├── er.mmd
├── sequence.mmd
├── deployment.mmd
├── architecture.c4
├── datasources.md
├── traceability.md
├── traceability-matrix.md
├── traceability.mmd
├── traceability.json
├── coverage.md
├── gaps.md
├── verification.json # written by `ddd verify`
├── verification.md # written by `ddd verify`
├── sourcemap.json # opt-in, only under `generate system --sourcemap`
└── snapshots/
└── <ts>-<guid>.loomsnap.json # provenance rule captures
None of the files in .loom/ ship to runtime; they are review and
tooling output. Check them into the project alongside the generated
code if you want pull-request-time diff visibility on wire contracts,
traceability coverage, or migration baselines.
Regeneration bookkeeping
| File | Producer | What it is |
|---|---|---|
manifest.json |
src/system/manifest.ts, written by the CLI write phase (src/cli/main.ts, phase ⑩) |
Every path this run emitted, sorted, with a scaffoldOnce flag on the files the user takes ownership of. This is how regeneration deletes. On the next run, a path the previous manifest lists and the current run does not emit is stale and is removed — so renaming an operation or a page no longer leaves a dead CommentHandler.cs / board.tsx behind to break the build. The prune is deliberately narrow: a file with no manifest entry (every hand-written file) is structurally unreachable, and .loomignored paths, scaffold-once files, migration files, and .loom/snapshots/ are all spared. An absent or unreadable manifest degrades to "prune nothing". Check it in — that is what makes the prune work on a teammate's clone. |
Wire contract
| File | Producer | What it is |
|---|---|---|
wire-spec.json |
src/system/wire-spec.ts (phase ⑨) |
JSON-Schema-shaped derivation from every aggregate / part / value object's wireShape. Language-agnostic; the canonical source of truth for what the JSON over the wire looks like. Diffable — wire-contract drift between regens shows up as a clean JSON diff. Carries the CONSTRAINTS the drift check needs, not just carrier types: an enum field publishes its declared members ({"type":"string","enum":["New","Qualified"]}, in declaration order, resolved in the referring context), so removing an enum value — a breaking change — moves the file instead of leaving it byte-identical. |
i18n catalog
| File | Producer | What it is |
|---|---|---|
messages.en.json |
src/system/i18n-catalog.ts (phase ⑨) |
Source-language (English) message catalog — every user-visible string extracted from the system's UIs, as a flat, key-sorted { key: message } object. Keys are content-hashed (page.<Page>.<role>.<hash> / component.<Comp>.<role>.<hash> / menu.<role>.<hash>, D-I18N-KEY) so the catalog is reorder-invariant and a rephrase re-keys cleanly (delete-old + add-new). The diff surface a future ddd i18n sync merges each locale against (M-T1.11, docs/old/proposals/i18n.md). This slice extracts plain string literals only — interpolated strings ("Order ${o.id}") await the template→ICU lowering slice. Extraction: src/generator/_walker/i18n-extract.ts. |
Storage routing
| File | Producer | What it is |
|---|---|---|
datasources.md |
src/system/datasources.ts → renderDataSourcesMd |
Per-system Markdown view of how resource declarations route domain contexts to physical storage. Two sections: per-deployable routing (context → kind → storage with effective schema / table-prefix), and per-storage usage roll-up. Catches accidental "primary" sharing and unused bindings at PR-review time. |
Diagrams
| File | Producer | What it is |
|---|---|---|
domain.mmd |
src/system/mermaid.ts → renderDomainDiagram |
Mermaid class diagram of every aggregate, value object, enum, and event in the system, with containments and X id references drawn as relationships. |
er.mmd |
src/system/mermaid.ts → renderErDiagram |
Mermaid entity-relationship diagram of every persisted aggregate + part — schema-shaped (PK / FK / columns), not class-shaped. |
workflows.mmd |
src/system/mermaid.ts → renderWorkflowDiagram |
One Mermaid flowchart per workflow, showing the create / load / mutate / emit steps. |
sequence.mmd |
src/system/mermaid.ts → renderSequenceDiagram |
Mermaid sequence diagram of an operation lifecycle (client → route → repository → DB → events). |
deployment.mmd |
src/system/mermaid.ts → renderDeploymentDiagram |
Mermaid deployment diagram of the system's deployables, their target backends, and their shared storage. |
architecture.c4 |
src/system/likec4.ts → renderC4Model |
LikeC4 model of the system at C4 levels 1–3 (context, container, component). Renderable in the LikeC4 viewer / VS Code extension. |
Traceability
| File | Producer | What it is |
|---|---|---|
traceability.md |
src/system/traceability.ts → renderTraceabilityDoc |
Per-requirement Markdown index: each requirement block with its linked solutions, testCases, and the runnable test / test e2e that verifies each test case. |
traceability-matrix.md |
src/system/traceability.ts → renderMatrix |
Two-axis matrix view (requirement × test case). |
traceability.mmd |
src/system/traceability.ts → renderTraceabilityDiagram |
Mermaid graph of the requirement → solution → testCase → test graph. |
traceability.json |
src/system/traceability.ts → renderTraceabilityJson |
Machine-readable form of the same graph (for CI gating, custom reports). |
coverage.md |
src/system/traceability.ts → renderCoverageReport |
Per-solution and per-requirement coverage roll-up. |
gaps.md |
src/system/traceability.ts → renderGapsReport |
Inverse of coverage — requirements with no solution, solutions with no test case, test cases with no test. |
verification.json |
ddd verify (src/cli/main.ts) |
Join of the requirements graph with a JSON of test results — every requirement gets a VERIFIED / FAILING / UNVERIFIED / UNTESTED verdict. |
verification.md |
ddd verify |
Human-readable companion to verification.json. |
See traceability.md for the surface that
populates these.
Snapshots
snapshots/ carries the immutable provenance captures:
| Pattern | Producer | What it is |
|---|---|---|
<ts>-<guid>.loomsnap.json |
ddd snapshot (src/system/loomsnap.ts) |
Provenance rule snapshot. One immutable timestamped+GUID file per system; captures the rule snapshots for every provenanced field. The latest such file is the one the generated runtime loads at startup. See provenance.md. |
They are only written by the explicit ddd snapshot sub-command — never
by generate.
Migration state is not under -o
.loom/ holds no migration state. The migration history is the
committed migrations/ directory beside the .ddd — recorded by
ddd migrate add, rendered by every generate system — so an output
tree can be deleted and regenerated anywhere. See
migrations.md § The migration chain.
Source map (opt-in)
sourcemap.json is the one artifact not written by default — it lands only
when generate system is passed --sourcemap. Emitted by src/system/sourcemap.ts
from an Origin spine that all five backends thread through their emitters, it maps
each generated line back to the .ddd construct — and, for operation bodies, the
.ddd statement — it came from, at both construct and statement granularity.
Its consumer is ddd trace <logfile>: given a runtime stack-trace from a generated
backend, it rewrites each frame to point at the originating .ddd source line, so a
failure in the generated code reads against the model the author actually wrote. See
tools.md for the CLI.
Note that "opt-in" is about the artifact, not the recorder. The web playground
records the map on every system generate and reads it in memory — that is what powers
its source ↔ output correspondence (playground.md) — while still
passing the flag off, so the emitted tree it shows and downloads stays byte-identical.
The distinction is possible because the Source Map v3 / JSR-45 sidecars (and the
trailing sourceMappingURL directives they add to the emitted .ts) are gated on
sourceTexts, not on the recorder: with a recorder and no sourceTexts, the only
addition to the emission is sourcemap.json itself.
What .loom/ is NOT
- Not deployed. Production projects don't read anything from
.loom/at runtime; the directory is build-time-only. The one exception is<ts>-<guid>.loomsnap.jsonfiles, which the Hono provenance SDK loads to render trace records — but they are loaded from a path inside the deployable's own folder during capture, not from.loom/at runtime. - Not regenerated incrementally. Every file in
.loom/is fully rewritten on eachgeneraterun. Don't hand-edit; pin via.loomignoreif you absolutely must keep a custom version. - Not part of the wire contract. The wire contract is what
wire-spec.jsondescribes, not the file itself. Backends emit the actual schemas (Zod, EF DTOs, Ecto schemas) directly into their own project trees.
Cross-references
generators.md§ Migrations — what the migration chain's entries become at backend emission time (per-platform output table andMigrationsIRplumbing).provenance.md— theddd snapshotcapture step and the runtime trace SDK.traceability.md— the requirement / solution / testCase surface that produces the traceability files.technical.md— phases ⑨ (composes<outdir>/) and ⑩ (writes everything via the.loomignorefilter).