# RuneWaker evaluated-pose audit `audit-animated-poses.mjs` checks the actual evaluated, skinned vertices of the RuneWaker actors shipped by HealerMan. It is intentionally separate from the clip-name/readiness audit: an animation can have the right name and duration while applying bone rotations in the wrong coordinate basis. ## What is audited The batch runner can discover packaged GLBs under `public/assets/creatures`, but the authoritative full-game input is the 322 runtime-referenced shipping paths in `artifacts/animation-audit/runtime-clip-coverage.json`. Discovery can include unused or non-combat assets and must not be substituted for that exact list in a release verdict. Each Meshopt shipping GLB is copied through glTF Transform to a temporary, uncompressed GLB because Blender does not import `EXT_meshopt_compression`. Blender then evaluates: - the armature rest/bind pose; - the first, middle, and final frame of every action by default; - finite and non-finite skinned vertices; - posed AABB, axis extents, diagonal, centroid, maximum radius, and RMS radius; - bind-relative size, radius, and centroid-shift ratios; - connected-topology component movement, resizing, and separation from the bind envelope. The temporary decompressed file is removed after each actor. The report hashes and identifies the original packaged GLB, not the temporary copy. ## Commands Run from `D:\Projects\HealerMan`. First regenerate the inventory and runtime selector plan from the same shipping build that will be pose-audited: ```powershell npm run runewaker:audit:animation-inventory npm run runewaker:audit:runtime-clips ``` The package aliases pass additional CLI arguments through to the batch runner and regenerate the deterministic all-dungeon report: ```powershell npm run runewaker:audit:pose-batch -- --input-list --jobs 2 npm run runewaker:audit:pose-master ``` The expected mapping is 436 entity templates to 268 base actor configurations to 322 runtime-referenced shipping variants. Do not continue if the inventory contract fails. Create the exact input list from the runtime plan, assert its size, and run the complete numeric audit plus semantic-family render pass: ```powershell $projectRoot = (Resolve-Path '.').Path $coverageFile = 'artifacts/animation-audit/runtime-clip-coverage.json' $coverage = Get-Content -Raw -LiteralPath $coverageFile | ConvertFrom-Json $inputs = @( foreach ($dungeon in $coverage.samplePlanByDungeonAndAsset.PSObject.Properties) { foreach ($asset in $dungeon.Value.PSObject.Properties) { (Join-Path $projectRoot $asset.Value.glb).Replace('\', '/') } } ) | Sort-Object -Unique if ($inputs.Count -ne 322) { throw "Expected 322 runtime shipping variants; found $($inputs.Count)." } $inputList = Join-Path $projectRoot 'artifacts/animation-audit/v5-pose-inputs.json' $inputJson = @{ inputs = @($inputs) } | ConvertTo-Json -Depth 3 [System.IO.File]::WriteAllText( $inputList, $inputJson + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false) ) node scripts/runewaker-pipeline/audit-animated-poses.mjs ` --root public/assets/creatures ` --input-list $inputList ` --output-dir artifacts/animation-audit/batches/v5-full ` --summary artifacts/animation-audit/batches/v5-full/summary.json ` --jobs 2 ` --sample-mode all ` --render-mode families ` --render-limit 24 ` --contact-sheets node scripts/runewaker-pipeline/make-pose-audit-review-pages.mjs ` artifacts/animation-audit/batches/v5-full ``` `--sample-mode all` numerically evaluates first, middle, and final samples from every action. `--render-mode families` renders a bounded representative semantic-family set for every shipping variant; it does not reduce the numeric action plan. Preserve the runtime coverage JSON, input list, pose reports, contact sheets, and review pages together. Their hashes identify the exact shipping build under review. Running discovery without an input list remains useful as a broader diagnostic, but its count is not the runtime contract: ```powershell node scripts/runewaker-pipeline/audit-animated-poses.mjs --jobs 2 ``` Audit one dungeon and keep a deterministic report for each packaged variant: ```powershell node scripts/runewaker-pipeline/audit-animated-poses.mjs ` --include '^paspers-shrine/' ` --jobs 2 ` --output-dir runewaker-export-work/pose-audit-paspers ``` Render one representative middle frame per detected semantic family and make a contact sheet: ```powershell node scripts/runewaker-pipeline/audit-animated-poses.mjs ` --input public/assets/creatures/paspers-shrine/fog-ferocity-03.glb ` --render-mode families ` --contact-sheets ``` Use `--render-mode flagged` to render only numeric outliers, or `--render-mode all` for every sampled frame. `--render-limit` bounds disk and rendering cost. Reports can be reused only when the shipping SHA-256 still matches by adding `--resume`. An inventory job can supply the exact runtime-used variant set as a JSON array or an object containing an `inputs` array: ```json { "inputs": [ "D:/Projects/HealerMan/public/assets/creatures/paspers-shrine/fog-ferocity-03.glb", { "file": "D:/Projects/HealerMan/public/assets/creatures/paspers-shrine/fog-loster-03.glb" } ] } ``` Pass it with `--input-list inventory.json`. Relative paths are resolved from the inventory file's directory. Input and Blender paths are converted to absolute paths before subprocesses start, so Blender changing its working directory cannot redirect a source or report. ## Required human visual review Numeric status is triage, not acceptance. A wrong quaternion basis can keep all values finite and leave the GLB Khronos-clean. A reviewer must inspect the contact sheet for every one of the 322 variant paths, including numeric passes, and compare warnings with their per-sample metrics. Any folded or inverted limb, exploded skin, detached equipment, discontinuity, or ambiguous extreme pose must be rerun with `--render-mode all` and, when necessary, compared with the original-client animation. After actual inspection, create a review input using `batch-a-visual-review-input.json` as the schema reference. Its `reviewedVariants` must exactly match `summary.json`; include both `demon-stronghold-125` and `zurhidon-stronghold-124` in `explicitEmptyDungeons`, and record a reason for each. The compiler verifies inventory equality but cannot perform or infer the visual review: ```powershell node scripts/runewaker-pipeline/compile-pose-audit-review.mjs ` artifacts/animation-audit/batches/v5-full ` artifacts/animation-audit/batches/v5-full/v5-visual-review-input.json ` artifacts/animation-audit/batches/v5-full/v5-review.json ` artifacts/animation-audit/batches/v5-full/v5-review.md ``` ## Completed v5 disposition `artifacts/animation-audit/post-v5/summary.json` contains all 322 runtime paths and 44,305 evaluated poses: 282 actors passed numerically, 40 are accepted-warning candidates, and there were zero errors and zero Blender/decompression/report failures. The 634 flagged samples exactly match the totals from the visually reviewed v4 batches; no new numeric deformation signature appeared after the v5 cast-semantics rebuild. Mantarick accounts for the expected static-pose warning, and Sardo's red Rune Warning Device retains its intentional native no-locomotion exception. The rebuilt inventory is green at 30/436/268/322 with zero blockers, and the runtime selector reports zero semantic near misses. The packaged/offline browser showed all 30 dungeons Ready and loaded both Pasper's Shrine and Hall of Survivors with no animation or WebGL errors. Only an unrelated library deprecation warning appeared. These results complete the v5 pose disposition; the sampling and original-client comparison limits below still apply. All 19 focused RuneWaker animation tests pass. The unfinished RoM class catalog and missing Manastorm fixtures remain unrelated repo-wide build/test blockers. ## Report contract Every `.pose-audit.json` is timestamp-free and deterministically ordered. `variantId` is the stable packaged path below the selected discovery root, so actors with the same basename in different dungeons remain distinct. Its `sampling.actionPlan` is the machine-readable audit plan: action index, exact action name, semantic family, action frame range, and every frame label and value selected for evaluation. `samples` records the resulting metrics, bind comparisons, and issue codes. `summary.status` is one of `pass`, `warning`, or `error`. The batch `summary.json` records one row per packaged input path. Numeric warnings do not fail the command because unusual animations can intentionally detach weapons or body parts. A Blender/decompression/report failure does fail the command. The documented Mantarick exception (Hall of Survivors template `102452`, asset `judgement-light-pose`) contains static posed meshes without an armature. It is still checked for finite geometry and bounds, and is reported as `static-pose-only` with a `pose-only-no-armature` warning rather than failing the batch. It is the only approved pose-only runtime variant. Sardo Castle template `105758` (`rune-device-red`, Rune Warning Device) is a native stationary device, not pose-only. Its source animation set intentionally has no locomotion. A missing-moving warning is acceptable only for this asset; its idle, configured combat, wound, and death actions still require numeric and visual review. ## Regression proof for the Pasper goatmen The preserved pre-fix v3 cache files and the shipping v4 files captured for the quaternion repair were audited with all actions: | Actor | Source | Poses | Flagged samples | | --- | --- | ---: | ---: | | Giant Assassin | pre-fix cache `2dd9e271a1296f39cb35864b.glb` | 61 | 51 | | Blackhorn Silencer | pre-fix cache `921ccbe5f2225bf2f02a3136.glb` | 61 | 51 | | Giant Assassin | v4 `paspers-shrine/fog-ferocity-03.glb` capture | 61 | 0 | | Blackhorn Silencer | v4 `paspers-shrine/fog-loster-03.glb` capture | 61 | 0 | The old poses were caught by connected-component movement even though their whole-character AABB diagonal stayed near the bind size. This is why global bounds alone are not sufficient for this regression. The v4 comparison proves the quaternion repair, while the completed 322-variant post-v5 audit proves the cast-semantics rebuild did not change the aggregate evaluated-pose result. ## Limits - Numeric shape checks cannot prove semantic correctness, timing quality, attack impact alignment, or that a clip is the intended RuneWaker clip. - Start/middle/end sampling can miss a defect isolated between those frames. - Family contact sheets render representative frames, not every frame of every action. Use `--render-mode all` for suspicious actors, then test timing and transitions in the browser. - Deliberately detached parts, projectiles, trails, and extreme attacks may require visual review rather than automatic rejection. - Component metrics require the evaluated modifier stack to preserve vertex count and ordering. - Contact sheets are evidence aids, not replacements for an in-game playtest.