The failure modes that shaped cluzter’s data model. All four share one shape: the output looks plausible and is wrong, with nothing anywhere reporting a problem. Three of them were already shipped by predecessor tooling; the fourth was designed out before it could be.

The common thread

A wiring diagram has no self-evident errors. A wrong colour, a duplicated connector or a short cut length all render as a perfectly normal-looking sheet. Every rule below is therefore structural — a type, a validation error, or an absent code path — never a convention, a comment, or a “remember to check”.

BL silently rendered black

The worst possible silent failure on a wiring diagram

WireViz has no BL code. In the jot → WireViz pipeline every wire jotted BL for blue fell through and rendered black. The predecessor’s own notes call this “the worst possible silent failure on a wiring diagram” — and they are right, because the diagram renders, the BOM generates, nothing warns, and the sheet you take to the car names the wrong wire.

The same trap exists in JIS notation, which is cluzter’s default standard. In JIS/Suzuki:

CodeColour
Bblack
Blblue

One character apart, and the shorter one is a prefix of the longer one. Any prefix-matching or fuzzy lookup resolves Bl to black. Lbl (light blue) and Lg (light green) sit in the same minefield, as does Br (brown) against B.

The rules, all three enforced structurally:

  1. Colour code lookup is exact-match only. Never prefix-match. Never fuzzy-match. Never normalise case in a way that collapses B and Bl.
  2. An unmatched code is a blocking validation error — the top row of the validation table, blocking every export. Never a fallback to a default colour, never a warning, never “black if unknown”.
  3. B vs Bl is a standing regression test. engine/colors/ gets table tests over every code in every standard, with the explicit assertion that B does not collide with Bl and that an unknown code throws.

For Agents

If you are touching colour lookup, the test that must not be softened is the one asserting an unknown code throws. A caller that wants a graceful degradation is asking for the exact bug this prevents. The predecessor fixed its instance by aliasing BL → BU; cluzter fixes the class by refusing to guess at all.

Why this drives the generated-colour design too: JIS has 14 authored codes and yields 196 base×tracer combinations (n + n(n−1) = n²). Colours are generated from the standard rather than enumerated, so there is exactly one place a code can be defined and exactly one place lookup can go wrong. The colour picker also shows a “used on this sheet” row specifically so a near-duplicate (B/Y vs Bl/Y) is not picked by accident.

Mono print without inline colour codes — the same failure class

A black-and-white sheet is unreadable without colour codes and looks entirely plausible: strokes, labels, connectors, title block, all present. It is the BL-renders-black failure wearing a different hat, and emitting one silently is the same category of harm.

Rule: mono mode forces labels on regardless of LOD tier.

Colour modeMono mode
Saturated strokeUniform black stroke
Colour is the identityColour code is the identity
Label optional, dropped by tierLabel mandatory, inline, on every segment
Dash pattern as a secondary cue only

Two things this pins down:

  • Tier-based label culling is overridden, not consulted. Mono print does not ask the LOD tier whether labels are wanted. Since T0/T1 legitimately drop wire labels for legibility, a mono export that respected tiers would produce exactly the unreadable-but-plausible sheet.
  • Dash patterns cannot carry the load alone. 30+ colours exhaust the distinguishable patterns, so patterns are a secondary cue on top of the code, never a replacement for it.

This is prevented structurally rather than left to discipline, for the same reason as the colour lookup: the person printing the sheet is the person least able to notice it is missing information.

Connector name as identity

ecu and ECU silently became two connectors

In the predecessor pipeline the connector name was the identity — no registry, no ID scheme, no declaration step. Same name across notes meant the same node, which is what made “jot separately, combine later” work at all. The cost of that mechanism is that a typo or a capitalisation slip silently creates a second connector, and the diagram renders happily with the wires split across two nodes that should be one.

Fix in cluzter: identity and display are separated.

FieldRole
idGenerated key. The only thing anything references. Never typed, never displayed as identity
designatorDisplay-only string. C101, ECU, whatever is convenient

Consequences that make the fix real rather than nominal:

  • Designator is not identity. Two connectors may share a designator and remain two connectors; nothing merges them silently.
  • A designator collision is a validation warning, not an error — visible in the diagnostics panel, clickable to select and zoom to the offender, but never blocking. Duplicated designators are sometimes legitimate mid-trace; silently merging them never is.
  • The same split applies one level down: cavityIndex is positional, stampedLabel is what is moulded on the part. Real connectors are numbered 7, 8, 9 with gaps; the identity must not depend on what is printed on the plastic.

Deriving wire length from a schematic path

A category error that produces a silently wrong cut list

The target diagram is schematic, not to scale — a tail lamp is drawn the same size as a generator, and runs are spaced for legibility rather than length. Measuring the drawn path of a wire and calling the result a wire length is therefore not an approximation, it is a category error: the number is meaningless, has the right units, and is indistinguishable from a real measurement once it lands in a CSV.

The output that consumes it is a cut list. Being wrong there means cut wire — the one place in this project where an error costs material rather than a redraw.

Rule: Wire.lengthMm is never derived from Wire.route. The model forbids it structurally.

  • route, trunk, every position, pitchMm and paperSize are paper mm.
  • lengthMm is world mm, and its only sources are a ruler on a calibrated underlay (two-point calibration → world-mm-per-paper-mm ratio) or a typed value.
  • There is no world-mm point type, because world mm is only ever a scalar. A mixed-unit expression is therefore a type error rather than something a reviewer has to notice.
  • No code path exists from a drawn path to a length. Not disabled — absent.

Two guards on top of the structural one:

  • A missing lengthMm blocks the cut list only — never the diagram, never drawing. A missing wire length must not stop drawing; it must absolutely stop cutting wire.
  • Print composition emits a “not to scale — lengths in cut list” note, so a reader cannot mistake the sheet for a dimensioned drawing.

Slack has the same discipline: applied per wire, then summed (ceil(lengthMm × (1 + slack)), default 5%), not as one percentage on the grand total. A 3 m run needs more absolute slack than a 200 mm one, and the cut list must show the length actually cut while Wire.lengthMm stays the measured value.

Adjacent gotchas from the same problem space

Not cluzter’s own, but they live next door and cost real time.

  • The retired netlists vs. the good scans. The homelab AI wiring-extraction pipeline is retired and its extracted netlists must not be trusted (kb-agent-api says so explicitly). That retirement applies to the AI-extracted netlists only — the OCR’d scanned manual pages remain good, and they are cluzter’s authoritative source for underlays. Do not read “wiring extraction is retired” as “the wiring scans are worthless”.
  • Scan resolution is the ceiling. The same extraction work found that at 1632×808 with ~9 systems per page, wire-colour stripe letters fail about 40% of the time (B/R vs B/Bl) — which is this note’s top gotcha appearing in OCR form. It is also the reason a human tracing over a calibrated underlay is the design, rather than an import.
  • Seeded standards are asserted, not verified. The ISO 8820 / SAE J1888 fuse colours and DIN 72552 relay terminal numbers ship from published standards. Worth one spot-check against the actual fuse box before a printed sheet is trusted to them.