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:
requires/excludescarry affordance logic: an edged form demands something grippable, because a blade nobody could hold is a blade nobody would have made.orderingcarries assembly logic, constraining which components can precede which along the primary axis.material-physicsrejects configurations that fail statics: the heavy head on the thin-walled vessel.ergonomicencodes the one constant every culture in every generated world shares, the human body. A long blade demands at least a medium grip because wrists are wrists everywhere; this is the only rule family that holds across all possible cultures by construction.
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.
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.
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.
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