ADR-0007 — Determinism and the geometry gate
The same YAML produces byte-identical output, and geometry defects fail the build rather than shipping.
Status: accepted
Two coupled invariants:
- Determinism. The same input produces byte-identical output — no timestamps, no randomness, no iteration-order dependence.
- The geometry gate. The scene is linted before rendering. L1 defects (node overlap on label-inclusive boxes, edges crossing foreign node interiors, endpoints not touching their node, port-chip overlap) fail the build. L2 auto-repairs; L3 advises.
Why
Determinism makes diagrams commitable. If output is stable, a built diagram can live in version control next to the code, and its diff is empty unless the network actually changed. Without it, every rebuild is noise and nobody commits the output — which removes the easiest way to publish a diagram.
It also enables the memoized page builds the dev server relies on: pages are content-hashed, so an edit rebuilds only the pages whose inputs moved.
The geometry gate makes a guarantee real. "Output is never overlapping" is either enforced or it's marketing. Since authors can't nudge anything (ADR-0001), they have no way to fix a bad layout by hand — so the compiler has to refuse to emit one. A diagram with overlapping nodes is worse than no diagram, because it looks authoritative while being unreadable.
Assumed: the "commitable diagrams" motivation is inferred. Determinism is stated as a hard requirement throughout the codebase without a recorded rationale.
Consequences
Text measurement cannot use a browser. @marrow/metrics measures with static
glyph tables at the exact pixel size and letter-spacing the renderers use. A DOM
measurement would vary by machine and break determinism. This is a real constraint
on anyone touching label sizing.
Sort everywhere order would be arbitrary. By id, consistently.
A build can fail for reasons that aren't the author's fault. A layout bug manifests as a hard failure, not a slightly ugly diagram. That's the intended trade — a loud failure is debuggable, a silently degraded one is not — but it does mean a compiler bug blocks a user completely.
The corpus is the enforcement layer. fixtures/corpus/ compiles every shape
family through the full pipeline and runs the linter over each result in the
test suite. An L1 defect anywhere fails npm test. Fixing a layout bug means
adding a fixture that reproduces it.
Moving a rule between L1 and L3 is a breaking change in practice, even though no API changes: builds that passed start failing, or defects that were blocked start shipping.
Considered and rejected
Warn on overlap and render anyway. Rejected: with no manual layout control, a warning gives the author nothing to act on.
Auto-repair everything. Rejected: L2 handles what's safely fixable. Silently "repairing" a genuine placement failure would hide the bug that caused it, and the repair would itself be non-obvious and hard to reason about.