Stage 9 · Description generation

The Engine Never Says “Dagger”

Stage 9 is the only stage the player ever reads, and it runs fresh on every inspection because the reader has changed since last time. Upstream stages build an honest object; this one presents it without handing over the answer key.

Neutral by construction

Every artefact carries a physicalLabel generated from observable properties alone: "short bronze elongated form with engraved disc-form attachment". The word "dagger" appears nowhere, because "dagger" is an interpretation and interpretive naming is the player's job. The occluded groundTruthTags exist solely so the engine can later judge claims against reality; no agent's interpretive model, player or NPC, ever reads them.

Registers: same facts, different foregrounding

Descriptions are filtered through registers, which shape framing without touching truth. The spec's worked example runs one wear pattern through all five:

They differ in the follow-up questions they invite. MVP ships three registers (observational, interpretive, technical); the five-register acquisition system with per-domain proficiency is designed and explicitly deferred.

raw data:marks along the edge neutral: "marks are visible along the edge" functional: "use-wear consistent with cutting or scraping" technical: "deformation from contact with harder material" rawData stays one drill-down away
Registers select framing over one set of facts. Nothing is added, nothing hidden; the follow-up question changes.

The modelling claim behind registers: expertise changes what a description affords rather than what is there. The functional reading points at use, the technical reading at manufacture, the ritual reading at intention, and each one licenses a different next action, which is the currency the game actually trades in.

Registers also explain how the lens stays legal. The pipeline guarantees forbid touching facts, so the only lever a belief system can pull is which true framing arrives first; registers are that lever, typed.

Registers run in both directions

When the player publishes, they choose a register too, and the choice trades credibility against reach:

The same mechanism that frames what the player reads frames what the profession hears from them.

One authoring constraint: a relative tag must never be described as an intrinsic property. A template rendering elite as "an object of obvious wealth" misrepresents what the score measured, which was standing against the producing culture's own norms. The constraint is written into the template contract, so the prose layer can't quietly undo the relative-tag semantics the classification page set out.

The lens sits on top

The interpretive lens (the game's core mechanic, specced in its own document) selects which template variants surface and how observations are ordered: high-salience observations up front, the rest behind investigation, suggested tags nudged by the player's published claims. The presentation type keeps the honest raw data one drill-down away at all times, and the lens's hard limits repeat from the overview: reorder, reframe, de-emphasise, nothing else. A player who has published "Culture A was a warrior society" will find blades foregrounded and weaving tools easy to scroll past. The engine isn't lying to them; it's agreeing with them, which is worse.

What exists today

The observational register is real, in four pieces:

Variant selection currently gates on parameter values only; register filtering, lens alignment and material-aware gating are the full description engine's job, a scheduled roadmap task that supersedes the minimal renderer.

// prose.ts: the slot convention the observational templates use
export const SLOT_PATTERN = /#([a-zA-Z][\w-]*?)(\.a)?#/g;
// "#taper# taper toward #point.a# point"
//   → "gradual taper toward a sharp point"
// any slot absent, or valued 'none' → the whole sentence is dropped,
// never rendered with a hole; observable 'none' values get their own
// slot-free variant instead
Where it lives src/lib/engine/generation/prose.ts (interim renderer) · templates: src/lib/data/descriptions/observational/ · Spec: doc 05 §12–13, doc 04 (lens) · Full engine: roadmap 2GN.38–39