Make-wrong-unrepresentable

Description

Replace a conditional runtime gate with a structural absence: instead of checking-and-rejecting an invalid state on each request, ensure the invalid state has no representation in the system’s construction at all. The handler doesn’t exist for the invalid path; the component doesn’t expose the invalid action; the type can’t hold the invalid value. The invariant is enforced by the topology of what’s constructible, not by predicates evaluated at runtime.

The structural shape: where an active-gate checks if (condition) reject, make-wrong-unrepresentable removes the code path entirely — there is no if to forget, no bypass to find, no flag to flip. The invalid state is literally absent from the state space rather than blocked at entry. This produces three advantages: the invariant can’t be accidentally bypassed, the check doesn’t need to be maintained across codebase evolution, and the resulting system’s surface is smaller (fewer exposed handles for invalid operations).

This is the hardest version of active-gate — not “gate on validation” but “remove the gate’s target domain from the system’s representable space.” The gate becomes structural rather than behavioral.

Composition

= shape (the structural topology that forecloses invalid states) + asymmetric-gate (the asymmetry between valid and invalid — now binary, not graded) + load-bearing (the missing handler is load-bearing by its absence).

Shape: the right structural design makes invalid states unrepresentable — it’s a shape question, not a logic question. Asymmetric-gate: the asymmetry is now absolute (invalid path doesn’t exist vs. valid path does); the cost asymmetry is maximal. Load-bearing: the absence of the handler is load-bearing — removing the handler would require adding it back, which would be the architectural regression.

Encounters

  • Handler-existence-as-invariant — UI component work: no onMouseDown on filled cells = literally can’t start a drag on an invalid target. The piece-on-cell component doesn’t expose the drag-initiation surface rather than exposing it and gating with if (cell.hasPiece) return. Stronger: harder to bypass, cleaner to read.
  • Type-system encoding of business rules — a NonEmptyList type vs. List with a runtime “must not be empty” check. The type makes the empty case unrepresentable at the point of construction; the check can be forgotten, the type cannot.
  • FSM state modeling — a finite state machine that doesn’t include an “invalid” state as a valid transition target; invalid transitions are not implemented rather than checked-and-rejected. Moving from if (transition.isValid()) to “invalid transitions don’t exist in the state table.”
  • Route-level access control — a route that doesn’t exist for unauthorized users (not routed to) vs. a route that checks authorization at entry. The former makes the unauthorized access path unrepresentable; the latter just blocks it.
  • Readonly typesReadonlyArray<T> in TypeScript vs. “don’t mutate this array” convention. The type makes mutation unrepresentable; the convention is a runtime gate waiting to be bypassed.
  • “Literally can’t” — James’s tell phrase in the corpus; appears consistently when the make-wrong-unrepresentable move is being applied or recognized.

When it applies / triggers on

User-initiated: User is discussing validation, invariants, or error cases — especially when the current approach is a runtime check that “should” prevent an invalid state. Also when user expresses a desire for “stronger” guarantees than the current check provides.

Agent-initiated: Engine detects a runtime gate pattern (check-and-reject) and evaluates whether the structural-absence alternative is available. Candidate inference: “this runtime gate is a candidate for make-wrong-unrepresentable — can the invalid state be excluded from the system’s representable space, so the check becomes unnecessary?”

Vocabulary cues: “make the wrong thing unrepresentable,” “illegal states,” “invalid state,” “structural invariant,” “encode in the type,” “handler doesn’t exist,” “literally can’t,” “harder to bypass,” “can’t be bypassed,” “remove the handler,” “structural enforcement,” “type-safe.”

Situation-shape signals: A runtime check or validation that fires on every request to block a specific invalid state. The form is indicated when the invalid state could be excluded from the construction space rather than blocked at each access. Strongest signal: if removing the check would require actively adding invalid-path code, the absence is already structural.

Composes with

  • shape (creation relationship) — the structural design that makes invalid states unrepresentable is a shape question: what topology forecloses the invalid path? Make-wrong-unrepresentable is a specific application of the shape primitive.
  • asymmetric-gate (specialization relationship) — the extreme case of asymmetric-gate where the asymmetry is binary (invalid path doesn’t exist) rather than graded (invalid path costs more). The active-gate form reduces to make-wrong-unrepresentable when the cost of the invalid path can be made infinite (by removing it).
  • active-gate-vs-passive-audit (specialization relationship) — make-wrong-unrepresentable is the most aggressive gate posture: not just “block at entry” but “remove the entry.” The form sits at the gate-maximalist end of the gate/audit spectrum.
  • graduation-promotion (creation relationship) — systems often evolve from runtime checks → active gates → make-wrong-unrepresentable as understanding of invariants matures. The graduation move produces structural enforcement as its mature form.
  • load-bearing (composition relationship) — the absent handler is load-bearing by its absence; removing it (adding the handler back) would be the architectural regression. Identifying which absences are load-bearing is the diagnostic move.

When it doesn’t apply

  • Transiently invalid states during construction — partial-builder patterns require states that are temporarily invalid during construction (a half-built object). Structural enforcement is too tight; runtime checks must take over during the construction phase.
  • Invariants that require global facts — some invariants can’t be made structural because they depend on facts only knowable at runtime (e.g., “no duplicate usernames” requires knowing all existing usernames). No local type can enforce this.
  • When the constraint is too tight — if the invalid state is needed in some contexts (e.g., testing, admin paths), making it unrepresentable forecloses legitimate uses. The form requires that the invalid state be truly invalid across all contexts.
  • In dynamic or interpreted contexts — some languages/frameworks don’t support structural enforcement at the level needed (e.g., purely dynamic schemas). Runtime gates may be the only practical option.

Sources

  • Yaron Minsky, “Effective ML” — “make illegal states unrepresentable” as a type-system design principle.
  • Scott Wlaschin, “Domain Modeling Made Functional” — F# type-system encoding of domain invariants.
  • FP tradition broadly: algebraic data types as a mechanism for structural constraint.
  • The specific instance that surfaced it in James’s corpus: handler-existence-as-invariant in UI component design (drag initiation on chess board).
  • The “literally can’t” tell — a James-specific verbal marker for this move appearing in the T13 discovery report.
  • Proposed name from the discovery report: make-wrong-unrepresentable preferred over unrepresentable-invariant as it names the move rather than the property.

Canonical exemplars from corpus (T2 2026-05-17)

Mined via scripts/mine_new_form_exemplars.py from data/backfilled-insights.jsonl. 5 backfill-only matches at score ≥ 2 — low support but high purity (the form’s tell-phrases “literally can’t” / “handler doesn’t exist” are unambiguous when they fire). The drag-select UI work in late April provides the densest exemplar cluster.

  • Handler-existence-as-invariant on filled cells (cwd: campconnect, 2026-04-18): “The handler-existence-as-invariant pattern (no onMouseDown on filled cells = literally can’t start a drag) is the cleanest version of ‘make the wrong thing unrepresentable.’ Production-planner work should adopt the same shape: piece-on-cell components don’t expose the drag-initiation surface, rather than exposing it and gating with if (cell.hasPiece) return. Cleaner code, harder to bypass.” — the form’s canonical instance: the invalid path does not exist, vs being checked-and-rejected.
  • Empty-cells-only as a binding rule, not a runtime check (cwd: campconnect, 2026-04-18): “DayCell only attaches onMouseDown when there’s no piece in the cell — so ‘mousedown on a piece doesn’t start a drag’ is enforced by what handlers exist, not by an if-check inside a handler. That’s the cleanest way to make an invariant unfalsifiable: if there’s no listener, the gesture literally can’t begin.” — sister exemplar in the same workstream, with the “literally can’t” tell.
  • PostHog env vars degrade to no-op (cwd: campconnect, 2026-05-01): “the install PR will reference NEXT_PUBLIC_POSTHOG_KEY + NEXT_PUBLIC_POSTHOG_HOST env vars that you’ll need to populate. The code degrades to a no-op when the env vars are absent (matches the existing GoogleAnalytics.tsx:13 pattern), so the install PR can land safely.” — adjacent form: the invalid state (missing creds) is handled-as-no-op rather than checked-and-rejected; weaker than make-wrong-unrepresentable but in the same family.

Trigger pattern (T2): Make-wrong-unrepresentable surfaces in UI-component / type-system / state-machine work where the user (or agent) notices that a runtime check could be replaced by structural absence. The “literally can’t” tell is high-precision but low-recall in the corpus; most instances cluster in late-April drag-select / planner work. Caveat: 5 matches is low; the catalog claim that the form generalizes to type systems / state machines / route-access-control is mostly backed by the canonical literature (Minsky, Wlaschin) rather than corpus instances.