Stage 5 · Normalisation & plausibility

Exhaustion Throws

The grammar is honest about geometry and ignorant of physics. Stage 5 flattens its tree into a normalised structure, then checks the result against physical plausibility rules. On failure it re-rolls; on exhaustion it refuses to emit anything at all.

Normalisation

Grammar expansion yields a tree; downstream stages want a flat, standardised shape. NormalisedArtefact carries the component list (each with its primitive type, parameters, position along the primary axis and arrangement-group membership), the attachment list, derived dimensions, portability and inspection depth. The spec also makes normalisation the owner of allowedMaterialTags per component, derived from a physical compatibility table: an edged elongated form admits metal and stone; a hollow-enclosed form admits clay, metal, wood and stone. Physics supplies these constraints; the grammar never did. In the shipped code the field is stubbed empty (its compatibility table is a scheduled task, 2GN.10, and the stub is labelled as such at the assignment site), and the convention downstream is that an empty list means "no constraint recorded yet" rather than "nothing fits".

The rule set

type PlausibilityRule =
  | { type: 'requires';  component: string; dependsOn: string }
  | { type: 'excludes';  component: string; excludes: string }
  | { type: 'ordering';  component: string; before?: string; after?: string }
  | { type: 'material-physics'; predicate: (a) => boolean; reason: string }
  | { type: 'ergonomic';        predicate: (a) => boolean; reason: string }

The rules encode things a craftsperson would never build: an edged form with nothing grippable attached, a long blade on a stubby grip, a heavy head lashed to a cord, a heavy component perched on a thin-walled vessel. Each predicate rule carries a human-readable reason; a failed check is a diagnostic. The five shapes are five kinds of impossibility:

The set is written negatively, and the direction is a design decision. Enumerating everything a craftsperson might build is open-ended; enumerating what nobody would build is tractable. The rules stay as ignorant of function as the grammar: they know certain geometry could never be used by human hands, whoever made it and whatever it was for.

expandGrammar normalise checkPlausibility → stage 6 PlausibilityExhaustedError pass fail: re-expand from stage 4, up to N attempts all N fail
Re-rolling is the intended control flow; exhaustion is a defect report, with the seed attached so it reproduces.

The exhaustion contract

When plausibility fails, the pipeline re-expands from stage 4 and tries again, up to N attempts. Grammar expansion is cheap; re-rolling is the intended control flow. If all N attempts fail, the contract (assigned to a scheduled roadmap task; checkPlausibility is shipped, the retry orchestration around it comes with the pipeline assembler) is to throw a typed PlausibilityExhaustedError carrying the seed, the attempt count and the last failing rule set.

Why throwing is correct here Exhaustion means the grammar and the rule set disagree: a generator defect to surface during development. A relaxed rule set would break the pipeline guarantee that every artefact reaching the player passes every plausibility rule. A fallback artefact not derived from the grammar would break Simulation Honesty, the design pillar that says all player-facing content derives from the same underlying data. The strongest case for a fallback is availability (a live game that throws is worse for the player than one that shows a slightly wrong pot); the project's answer is that the error is a development-time signal by construction, because determinism means any exhausting seed can be reproduced and fixed before release.

The guarantee

The pipeline promises structural coherence unconditionally: no artefact that fails a plausibility rule ever reaches the player, and the promise holds vacuously through exhaustion because in that case nothing reaches the player.

Where it lives src/lib/engine/generation/plausibility.ts (checkPlausibility, rule predicates) · rules data: src/lib/data/plausibility.ts · normalisation: grammar.ts's normaliseArtefact · Spec: doc 05 §6, §14