# ADR-0018: Quaternius Stage 2 — adopt source proportions at runtime - **Status:** Accepted (2026-11-05, owner chose option 2) - **Date:** 2026-11-05 - **Supersedes:** ADR-0017 decision 2 (Stage 1 keeps the 13-core contract) ## Context ADR-0017 made Quaternius the NPC base and staged the migration: Stage 1 bakes a proportion-warped mesh onto the existing 13-core contract; Stage 2 adopts the 62-joint rig at runtime only if the warp fails. The Stage 1 proportion warp was executed and measured (`docs/assets/npc-model-sourcing.md` §Rest-pose and proportion-warp measurement). The arm-down rest pose bakes cleanly, but the warp to the contract proportions does not: | Joint | Quaternius | Contract | Delta | | --- | --- | --- | --- | | Hips height | 0.869 | 0.62 | −0.249 | | Shoulder x | 0.148 | 0.27 | +0.122 | | Arm (shoulder→hand tip) | 0.429 | 0.70 | ×1.63 | | Leg (hip→ankle) | 0.835 | 0.62 | ×0.74 | | Torso (hip→head) | 0.745 | 1.20 | ×1.61 | The vision judge reads the warped mesh as distorted: paddle/claw hands, forearms lost into the torso, stretched shins, "long legs + short torso". The arm either stays short (a shortened arm against the contract hand) or stretches 1.63× (a distorted deltoid + hand). Both are the gate's rejection conditions. ## Decision Stop the Stage 1 rollout and **adopt the Quaternius 62-joint rig at runtime** (owner chose option 2). The mesh is not proportion-warped: it keeps its source LOD (3,199 welded verts / 6,206 tris) and only the arm-down rest pose is baked. - `NpcAvatar` maps the Quaternius joint names instead of the 13-core names. - Gear attach points are re-derived from the Quaternius skeleton (hip ≈0.87, shoulder ≈0.148/1.39, wrist ≈0.99, knee ≈0.52). - The 24 upstream clips ship as authoring reference only and are **not played at runtime**. `NpcAvatar._animate()` drives the proxy pivots and `_sync_skeleton()` writes the bone poses, which is what the FSM already needed for sitting, raising a hand and turning the head. Playing the clips would replace that state machine rather than supplement it; revisiting this needs its own ADR. Option 1 (keep the 13-core names with Quaternius rest positions) is rejected in favour of the full source rig. ## Consequences ### Positive - The Quaternius silhouette, deltoid, hand and leg proportions are preserved — no paddle hands, no shortened arm, no torso/leg distortion. - The mesh stays at source LOD (far under the 24,000 cap), so no decimation is needed. ### Negative - The runtime contract numbers (hip 0.62, shoulder 0.27, hand 0.78) change; every gear attach point, `npc_proportions` guide, fit measurement and pose-composition constant must be re-derived and re-tested. - `NpcAvatar`'s bone mapping and pose composition are rewritten for the 62-joint rig, a larger change than the mesh swap. - The 12-variant rollout and the 12×6 baseline are still blocked until the new rest positions and gear fit are owner-approved. ## Alternatives - **Ship the warped mesh.** Rejected by the gate: the 1.63× arm / 0.74× leg warp visibly distorts and the owner directed keeping the Quaternius as close to source as possible. - **Keep the old contract and accept a short arm.** Rejected for the same reason: the hand ends at ≈0.99 instead of 0.78, so handheld gear floats unless attach points move — which is option 1. - **Abandon Quaternius and keep the procedural generator.** Rejected in ADR-0017. ## Open question Resolved: option 2 (62-joint runtime) is the owner's choice. The first implementation step is the `NpcAvatar` bone-map + gear-attach re-derivation, then the slot/AO bake and the 12-variant rollout. ## Follow-up decision: full roster swap - **Date:** 2026-11-05 (owner, at the `0.9.9` face/silhouette gate) The owner chose **option (a) full swap** from the face/silhouette gate (its sheets were retired with the review pages): every roster character moves onto the Quaternius base, and the procedural generator plus the MakeHuman head retire to the tested fallback rather than staying in the shipped roster. Scope consequences: - The shipped roster keeps its twelve public character names and `CHARACTER_POOLS` mapping; the geometry behind them comes from the three CC0 full-body sources already researched (`Casual`, `Casual_Hoodie`, `Worker`), aliased per role. - Variety now comes from the five tint slots, the gear library and the per-role outfit rules instead of twelve distinct meshes. Roster silhouettes are therefore *less* varied than the procedural library until the modular part library (hair, outfits) is added — tracked as a `0.9.10` task, not hidden. - The procedural library, `_build_body()` and the MakeHuman head stay in the tree and tested as the instant fallback; no MPFB or Stage-1 proportion-warp work reopens.