#!/usr/bin/env node
/**
* Deterministically merge the RuneWaker actor inventory, runtime selector audit,
* and all four evaluated-pose batches into one durable master report.
*
* The generator is intentionally all-or-nothing. It never emits a partial master
* report when a batch summary/review is absent, malformed, internally inconsistent,
* or missing a disposition for a warning/error actor.
*/
import { createHash } from "node:crypto";
import { access, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
const projectRoot = path.resolve(import.meta.dirname, "..", "..");
const artifactRoot = path.join(projectRoot, "artifacts", "animation-audit");
const outputJsonPath = path.join(
artifactRoot,
"ALL_RUNEWAKER_ACTOR_POSE_AUDIT.json",
);
const outputMarkdownPath = path.join(
artifactRoot,
"ALL_RUNEWAKER_ACTOR_POSE_AUDIT.md",
);
const expectedTotals = Object.freeze({
dungeons: 30,
entityTemplates: 436,
actorContracts: 268,
runtimeUsedVariants: 322,
emptyCopyZones: 2,
});
const sourceFiles = [
"artifacts/animation-audit/inventory.json",
"artifacts/animation-audit/runtime-clip-coverage.json",
"artifacts/animation-audit/post-v5/summary.json",
...["a", "b", "c", "d"].flatMap((batch) => [
`artifacts/animation-audit/batches/batch-${batch}/summary.json`,
`artifacts/animation-audit/batches/batch-${batch}/batch-${batch}-review.json`,
]),
];
function fail(message) {
throw new Error(`[master-actor-pose-audit] ${message}`);
}
function invariant(condition, message) {
if (!condition) fail(message);
}
function toProjectPath(absolutePath) {
return path.relative(projectRoot, absolutePath).replaceAll("\\", "/");
}
function absoluteProjectPath(relativePath) {
return path.resolve(projectRoot, relativePath);
}
function normalizedVariantId(value) {
return String(value ?? "")
.replaceAll("\\", "/")
.replace(/^public\/assets\/creatures\//, "")
.replace(/^\/assets\/creatures\//, "");
}
function variantIdToKey(variantId) {
const normalized = normalizedVariantId(variantId);
const match = /^([^/]+)\/(.+)\.glb$/i.exec(normalized);
invariant(Boolean(match), `Invalid shipping variant id: ${variantId}`);
return `${match[1]}:${match[2]}`;
}
function sorted(values, selector = (value) => String(value)) {
return [...values].sort((left, right) =>
selector(left).localeCompare(selector(right), "en"),
);
}
function asArray(value) {
if (value === undefined || value === null) return [];
return Array.isArray(value) ? value : [value];
}
function stringList(value) {
return asArray(value)
.map((entry) => String(entry))
.filter(Boolean);
}
function markdownCell(value) {
if (value === undefined || value === null || value === "") return "-";
return String(value)
.replaceAll("|", "\\|")
.replaceAll("\r\n", "
")
.replaceAll("\n", "
");
}
function markdownCode(value) {
if (value === undefined || value === null || value === "") return "-";
return `\`${String(value).replaceAll("`", "\\`")}\``;
}
function sha256(buffer) {
return createHash("sha256").update(buffer).digest("hex");
}
async function readJsonSource(relativePath) {
const absolutePath = absoluteProjectPath(relativePath);
let buffer;
try {
buffer = await readFile(absolutePath);
} catch (error) {
fail(`Unable to read ${relativePath}: ${error.message}`);
}
let value;
try {
value = JSON.parse(buffer.toString("utf8"));
} catch (error) {
fail(`Invalid JSON in ${relativePath}: ${error.message}`);
}
return {
path: relativePath.replaceAll("\\", "/"),
sha256: sha256(buffer),
value,
};
}
function mergeReviewRecord(index, item, source) {
const variantId = normalizedVariantId(
item?.variantId ?? item?.variant ?? item?.actor ?? item?.input,
);
if (!variantId || !variantId.includes("/")) return;
const observations = asArray(item?.observations).map((entry) => String(entry));
const disposition =
item?.disposition ??
item?.visualNote ??
item?.note ??
(observations.length > 0 ? observations.join(" ") : null);
const incoming = {
source,
visualVerdict:
item?.visualVerdict ?? item?.overallVerdict ?? item?.verdict ?? null,
disposition: disposition ? String(disposition) : null,
observations,
};
const existing = index.get(variantId) ?? {
source: [],
visualVerdict: null,
disposition: null,
observations: [],
};
index.set(variantId, {
source: [...new Set([...asArray(existing.source), source])],
visualVerdict: incoming.visualVerdict ?? existing.visualVerdict,
disposition: incoming.disposition ?? existing.disposition,
observations: [...new Set([...existing.observations, ...observations])],
});
}
function reviewIndex(review) {
const index = new Map();
for (const item of asArray(review.actors)) {
mergeReviewRecord(index, item, "review.actors");
}
for (const item of asArray(review.actorVerdicts)) {
mergeReviewRecord(index, item, "review.actorVerdicts");
}
for (const item of asArray(review.warningActors)) {
mergeReviewRecord(index, item, "review.warningActors");
}
for (const item of asArray(review.warningDispositions)) {
mergeReviewRecord(index, item, "review.warningDispositions");
}
return index;
}
function globalVisualVerdict(review) {
if (review.visualReview?.verdict) return review.visualReview.verdict;
if (review.totals?.visualErrors === 0 && review.totals?.visualPass > 0) {
return "pass";
}
if (
review.totals?.confirmedDefects === 0 &&
review.totals?.visualAccepted > 0
) {
return "pass";
}
return review.result ?? review.conclusions?.verdict ?? null;
}
function reviewLimitations(review) {
return [
...stringList(review.limitations),
...stringList(review.conclusions?.limitations),
];
}
function compactRuntimeVariant(dungeonId, assetId, entry) {
const baseSelections = Object.fromEntries(
sorted(Object.entries(entry.base ?? {}), ([name]) => name).map(
([state, selection]) => [
state,
{
name: selection?.name ?? null,
requestedState: selection?.requestedState ?? null,
resolvedState: selection?.resolvedState ?? null,
exact: selection?.exact ?? null,
},
],
),
);
const combatSelections = Object.fromEntries(
sorted(Object.entries(entry.combat ?? {}), ([name]) => name).map(
([kind, selection]) => [
kind,
{
selection: selection?.selection ?? null,
revisionCycle: asArray(selection?.revisionCycle).map((cycle) => ({
revision: cycle.revision,
name: cycle.name,
})),
clips: asArray(selection?.clips).map((clip) => clip.name),
},
],
),
);
return {
key: `${dungeonId}:${assetId}`,
dungeonId,
actorId: entry.actorId,
assetId,
baseActorId: entry.baseActorId,
appearanceSignature: entry.appearanceSignature ?? null,
templateIds: [...asArray(entry.templateIds)].sort((a, b) => a - b),
url: entry.url,
glb: entry.glb,
glbSha256: entry.glbSha256,
animationMode: entry.animationMode,
configuredAttackAnimationKinds: sorted(
asArray(entry.configuredAttackAnimationKinds),
),
standardFamilyGaps: sorted(asArray(entry.standardFamilyGaps)),
semanticNearMisses: entry.semanticNearMisses ?? {},
manifestIsAuthoritative: entry.manifestIsAuthoritative,
manifestClipCount: entry.manifestClipCount,
manifestGlbExactOrderMatch: entry.manifestGlbExactOrderMatch,
manifestAnimations: asArray(entry.manifestAnimations),
baseSelections,
combatSelections,
runtimeNotes: entry.runtimeNotes ?? {},
flags: asArray(entry.flags),
};
}
const missingSources = [];
for (const relativePath of sourceFiles) {
try {
await access(absoluteProjectPath(relativePath));
} catch {
missingSources.push(relativePath);
}
}
if (missingSources.length > 0) {
fail(
`Master report not written because required inputs are missing:\n${missingSources
.map((entry) => ` - ${entry}`)
.join("\n")}\nComplete every Batch A/B/C/D summary and visual review first.`,
);
}
const loadedSources = new Map();
for (const relativePath of sourceFiles) {
loadedSources.set(relativePath, await readJsonSource(relativePath));
}
const inventory = loadedSources.get(
"artifacts/animation-audit/inventory.json",
).value;
const runtimeCoverage = loadedSources.get(
"artifacts/animation-audit/runtime-clip-coverage.json",
).value;
const postV5Source = loadedSources.get(
"artifacts/animation-audit/post-v5/summary.json",
);
const postV5Summary = postV5Source.value;
invariant(
inventory.dungeons?.length === expectedTotals.dungeons,
`inventory.json must contain ${expectedTotals.dungeons} dungeons; found ${inventory.dungeons?.length ?? 0}`,
);
invariant(
inventory.entityTemplates?.length === expectedTotals.entityTemplates,
`inventory.json must contain ${expectedTotals.entityTemplates} entity templates; found ${inventory.entityTemplates?.length ?? 0}`,
);
invariant(
inventory.actorPackages?.length === expectedTotals.actorContracts,
`inventory.json must contain ${expectedTotals.actorContracts} actor contracts; found ${inventory.actorPackages?.length ?? 0}`,
);
invariant(
inventory.shippingAssetVariants?.length === expectedTotals.runtimeUsedVariants,
`inventory.json must contain ${expectedTotals.runtimeUsedVariants} shipping variants; found ${inventory.shippingAssetVariants?.length ?? 0}`,
);
invariant(
inventory.emptyCopyZones?.length === expectedTotals.emptyCopyZones,
`inventory.json must contain ${expectedTotals.emptyCopyZones} empty copy zones; found ${inventory.emptyCopyZones?.length ?? 0}`,
);
invariant(
runtimeCoverage.totals?.dungeons === expectedTotals.dungeons &&
runtimeCoverage.dungeons?.length === expectedTotals.dungeons,
`runtime-clip-coverage.json must enumerate ${expectedTotals.dungeons} dungeons`,
);
invariant(
runtimeCoverage.totals?.configuredTemplates === expectedTotals.entityTemplates &&
runtimeCoverage.templates?.length === expectedTotals.entityTemplates,
`runtime-clip-coverage.json must enumerate ${expectedTotals.entityTemplates} entity templates`,
);
invariant(
runtimeCoverage.totals?.configuredBaseActors === expectedTotals.actorContracts,
`runtime-clip-coverage.json must declare ${expectedTotals.actorContracts} base actor contracts`,
);
invariant(
runtimeCoverage.totals?.configuredActorAssets ===
expectedTotals.runtimeUsedVariants,
`runtime-clip-coverage.json must declare ${expectedTotals.runtimeUsedVariants} runtime-used variants`,
);
const runtimeVariants = [];
for (const [dungeonId, assets] of sorted(
Object.entries(runtimeCoverage.samplePlanByDungeonAndAsset ?? {}),
([name]) => name,
)) {
for (const [assetId, entry] of sorted(Object.entries(assets), ([name]) => name)) {
runtimeVariants.push(compactRuntimeVariant(dungeonId, assetId, entry));
}
}
invariant(
runtimeVariants.length === expectedTotals.runtimeUsedVariants,
`runtime sample plan must contain ${expectedTotals.runtimeUsedVariants} variants; found ${runtimeVariants.length}`,
);
const runtimeVariantByKey = new Map(
runtimeVariants.map((entry) => [entry.key, entry]),
);
const inventoryVariantByKey = new Map(
inventory.shippingAssetVariants.map((entry) => [entry.key, entry]),
);
invariant(
inventoryVariantByKey.size === expectedTotals.runtimeUsedVariants,
"inventory shipping variant keys are not unique",
);
for (const key of inventoryVariantByKey.keys()) {
invariant(runtimeVariantByKey.has(key), `Runtime sample plan is missing ${key}`);
}
for (const key of runtimeVariantByKey.keys()) {
invariant(inventoryVariantByKey.has(key), `Inventory is missing runtime variant ${key}`);
}
const runtimeTemplateByKey = new Map(
runtimeCoverage.templates.map((entry) => [
`${entry.dungeonId}:${entry.templateId}`,
entry,
]),
);
invariant(
runtimeTemplateByKey.size === expectedTotals.entityTemplates,
"runtime entity-template keys are not unique",
);
for (const template of inventory.entityTemplates) {
invariant(
runtimeTemplateByKey.has(template.key),
`Runtime coverage is missing entity template ${template.key}`,
);
}
const batchRecords = [];
const poseAuditByVariantKey = new Map();
const allAuditIssues = [];
const missingDispositions = [];
for (const batchLetter of ["a", "b", "c", "d"]) {
const batchId = `batch-${batchLetter}`;
const batchDirectory = path.join(artifactRoot, "batches", batchId);
const summarySource = loadedSources.get(
`artifacts/animation-audit/batches/${batchId}/summary.json`,
);
const reviewSource = loadedSources.get(
`artifacts/animation-audit/batches/${batchId}/${batchId}-review.json`,
);
const summary = summarySource.value;
const review = reviewSource.value;
const indexedReview = reviewIndex(review);
const batchGlobalVisualVerdict = globalVisualVerdict(review);
invariant(
summary.totals?.actors === summary.actors?.length,
`${batchId} summary actor total does not match its actor array`,
);
const statusCounts = summary.actors.reduce(
(counts, actor) => {
counts[actor.status] = (counts[actor.status] ?? 0) + 1;
return counts;
},
{},
);
for (const status of ["pass", "warning", "error", "failed"]) {
invariant(
(summary.totals?.[status] ?? 0) === (statusCounts[status] ?? 0),
`${batchId} ${status} total does not match its actor array`,
);
}
const batchIssues = [];
const batchActors = [];
for (const actor of sorted(summary.actors, (entry) => entry.input)) {
const variantId = normalizedVariantId(actor.input);
const variantKey = variantIdToKey(variantId);
invariant(
!poseAuditByVariantKey.has(variantKey),
`Shipping variant ${variantKey} appears in more than one pose-audit batch`,
);
const actorReview = indexedReview.get(variantId) ?? {
source: "batch-global",
visualVerdict: batchGlobalVisualVerdict,
disposition: null,
observations: [],
};
const actorIssues = [];
let report = null;
let reportSha256 = null;
if (actor.report) {
const reportRelativePath = `artifacts/animation-audit/batches/${batchId}/${normalizedVariantId(actor.report)}`;
const reportAbsolutePath = absoluteProjectPath(reportRelativePath);
try {
const reportBuffer = await readFile(reportAbsolutePath);
reportSha256 = sha256(reportBuffer);
report = JSON.parse(reportBuffer.toString("utf8"));
} catch (error) {
if (actor.status !== "failed") {
fail(`Unable to read ${reportRelativePath}: ${error.message}`);
}
}
if (report) {
for (const [sampleIndex, sample] of asArray(report.samples).entries()) {
for (const [issueIndex, issue] of asArray(sample.issues).entries()) {
actorIssues.push({
batch: batchId,
variantId,
variantKey,
source: "sample",
sampleIndex,
issueIndex,
sampleId: sample.sampleId,
action: sample.action,
semanticFamily: sample.semanticFamily,
frameLabel: sample.frameLabel,
frame: sample.frame,
severity: issue.severity,
code: issue.code,
value: issue.value ?? null,
detail: issue.detail ?? issue.message ?? null,
bindComparison: sample.bindComparison
? {
diagonalRatio: sample.bindComparison.diagonalRatio ?? null,
maximumRadiusRatio:
sample.bindComparison.maximumRadiusRatio ?? null,
rmsRadiusRatio: sample.bindComparison.rmsRadiusRatio ?? null,
centroidShiftRatio:
sample.bindComparison.centroidShiftRatio ?? null,
axisExtentRatios:
sample.bindComparison.axisExtentRatios ?? null,
componentCount: sample.bindComparison.componentCount ?? null,
extremeComponentCount:
sample.bindComparison.extremeComponentCount ?? null,
disconnectedComponentCount:
sample.bindComparison.disconnectedComponentCount ?? null,
}
: null,
});
}
}
for (const [issueIndex, issue] of asArray(report.assetIssues).entries()) {
actorIssues.push({
batch: batchId,
variantId,
variantKey,
source: "asset",
sampleIndex: null,
issueIndex,
sampleId: null,
action: null,
semanticFamily: null,
frameLabel: null,
frame: null,
severity: issue.severity,
code: issue.code,
value: issue.value ?? null,
detail: issue.detail ?? issue.message ?? null,
bindComparison: null,
});
}
for (const [issueIndex, issue] of asArray(
report.summary?.evaluationWarnings,
).entries()) {
actorIssues.push({
batch: batchId,
variantId,
variantKey,
source: "evaluation",
sampleIndex: null,
issueIndex,
sampleId: null,
action: null,
semanticFamily: null,
frameLabel: null,
frame: null,
severity: issue?.severity ?? "warning",
code: issue?.code ?? "evaluation-warning",
value: issue?.value ?? null,
detail:
typeof issue === "string"
? issue
: issue?.detail ?? issue?.message ?? JSON.stringify(issue),
bindComparison: null,
});
}
const reportWarnings = actorIssues.filter(
(issue) => issue.severity === "warning",
).length;
const reportErrors = actorIssues.filter(
(issue) => issue.severity === "error",
).length;
invariant(
reportWarnings === (report.summary?.warnings ?? 0),
`${variantId} report warning count ${report.summary?.warnings ?? 0} does not match ${reportWarnings} extracted records`,
);
invariant(
reportErrors === (report.summary?.errors ?? 0),
`${variantId} report error count ${report.summary?.errors ?? 0} does not match ${reportErrors} extracted records`,
);
invariant(
(actor.warnings ?? 0) === reportWarnings,
`${variantId} summary/report warning counts disagree`,
);
invariant(
(actor.errors ?? 0) === reportErrors,
`${variantId} summary/report error counts disagree`,
);
}
}
if (actor.failure) {
actorIssues.push({
batch: batchId,
variantId,
variantKey,
source: "process-failure",
sampleIndex: null,
issueIndex: 0,
sampleId: null,
action: null,
semanticFamily: null,
frameLabel: null,
frame: null,
severity: "error",
code: "pose-audit-process-failure",
value: null,
detail: String(actor.failure),
bindComparison: null,
});
}
if (["warning", "error", "failed"].includes(actor.status)) {
if (!actorReview.disposition) {
missingDispositions.push(`${batchId}:${variantId}`);
}
}
const poseAudit = {
batch: batchId,
variantId,
status: actor.status,
actions: actor.actions,
poses: actor.poses,
flaggedSamples: actor.flaggedSamples,
warnings: actor.warnings,
errors: actor.errors,
failure: actor.failure ?? null,
report: actor.report
? `artifacts/animation-audit/batches/${batchId}/${normalizedVariantId(actor.report)}`
: null,
reportSha256,
sourceGlbSha256: report?.source?.sha256 ?? null,
visualReview: actorReview,
issues: actorIssues,
};
poseAuditByVariantKey.set(variantKey, poseAudit);
batchActors.push(poseAudit);
batchIssues.push(...actorIssues);
allAuditIssues.push(...actorIssues);
}
const batchWarningRecords = batchIssues.filter(
(issue) => issue.severity === "warning",
).length;
const batchErrorRecords = batchIssues.filter(
(issue) => issue.severity === "error",
).length;
const actorWarningRecords = batchActors.reduce(
(total, actor) => total + (actor.warnings ?? 0),
0,
);
const actorErrorRecords = batchActors.reduce(
(total, actor) => total + (actor.errors ?? 0),
0,
);
invariant(
batchWarningRecords === actorWarningRecords,
`${batchId} extracted warning-record total is inconsistent`,
);
invariant(
batchErrorRecords >= actorErrorRecords,
`${batchId} extracted error-record total is inconsistent`,
);
batchRecords.push({
batch: batchId,
summaryPath: summarySource.path,
reviewPath: reviewSource.path,
summarySha256: summarySource.sha256,
reviewSha256: reviewSource.sha256,
dungeons: sorted(
new Set(batchActors.map((actor) => actor.variantId.split("/")[0])),
),
totals: summary.totals,
numericWarningRecords: batchWarningRecords,
numericErrorRecords: batchErrorRecords,
globalVisualVerdict: batchGlobalVisualVerdict,
reviewSummary: {
totals: review.totals ?? null,
disposition: review.disposition ?? null,
conclusions: review.conclusions ?? null,
visualReview: review.visualReview ?? null,
poseDefects: asArray(review.poseDefects),
sourceAuthoredComponentEvidence: asArray(
review.sourceAuthoredComponentEvidence,
),
knownRuntimeNamingOrCoverageGaps: asArray(
review.knownRuntimeNamingOrCoverageGaps,
),
emptyDungeons: asArray(review.emptyDungeons),
result: review.result ?? null,
},
limitations: reviewLimitations(review),
});
}
if (missingDispositions.length > 0) {
fail(
`The following warning/error actors have no explicit visual disposition:\n${missingDispositions
.map((entry) => ` - ${entry}`)
.join("\n")}`,
);
}
invariant(
poseAuditByVariantKey.size === expectedTotals.runtimeUsedVariants,
`Pose batches must contain ${expectedTotals.runtimeUsedVariants} unique variants; found ${poseAuditByVariantKey.size}`,
);
for (const key of inventoryVariantByKey.keys()) {
invariant(poseAuditByVariantKey.has(key), `Pose batches are missing ${key}`);
}
for (const key of poseAuditByVariantKey.keys()) {
invariant(inventoryVariantByKey.has(key), `Pose batches contain unknown variant ${key}`);
}
const baselineTotals = {
actors: batchRecords.reduce((total, entry) => total + entry.totals.actors, 0),
pass: batchRecords.reduce((total, entry) => total + entry.totals.pass, 0),
warning: batchRecords.reduce(
(total, entry) => total + entry.totals.warning,
0,
),
error: batchRecords.reduce((total, entry) => total + entry.totals.error, 0),
failed: batchRecords.reduce((total, entry) => total + entry.totals.failed, 0),
poses: batchRecords.reduce((total, entry) => total + entry.totals.poses, 0),
flaggedSamples: batchRecords.reduce(
(total, entry) => total + entry.totals.flaggedSamples,
0,
),
};
for (const field of [
"actors",
"pass",
"warning",
"error",
"failed",
"poses",
"flaggedSamples",
]) {
invariant(
postV5Summary.totals?.[field] === baselineTotals[field],
`Post-v5 aggregate ${field}=${postV5Summary.totals?.[field]} does not match Batch A-D baseline ${baselineTotals[field]}`,
);
}
invariant(
postV5Summary.actors?.length === expectedTotals.runtimeUsedVariants,
`Post-v5 summary must enumerate ${expectedTotals.runtimeUsedVariants} actors; found ${postV5Summary.actors?.length ?? 0}`,
);
const postV5ActorByKey = new Map();
for (const actor of postV5Summary.actors) {
const variantId = normalizedVariantId(actor.input);
const variantKey = variantIdToKey(variantId);
invariant(
!postV5ActorByKey.has(variantKey),
`Post-v5 summary contains duplicate variant ${variantKey}`,
);
postV5ActorByKey.set(variantKey, { ...actor, variantId });
}
const postV5ComparedFields = [
"status",
"actions",
"poses",
"flaggedSamples",
"errors",
"warnings",
];
const postV5Mismatches = [];
const postV5VariantComparisons = [];
for (const variantKey of sorted(poseAuditByVariantKey.keys())) {
const baseline = poseAuditByVariantKey.get(variantKey);
const postV5 = postV5ActorByKey.get(variantKey);
if (!postV5) {
postV5Mismatches.push({
variantKey,
field: "variant",
baseline: baseline.variantId,
postV5: null,
});
continue;
}
const comparison = {
variantKey,
variantId: baseline.variantId,
batch: baseline.batch,
exactMatch: true,
baseline: {},
postV5: {},
postV5Report: `artifacts/animation-audit/post-v5/${normalizedVariantId(postV5.report)}`,
};
for (const field of postV5ComparedFields) {
comparison.baseline[field] = baseline[field];
comparison.postV5[field] = postV5[field];
if (baseline[field] !== postV5[field]) {
comparison.exactMatch = false;
postV5Mismatches.push({
variantKey,
field,
baseline: baseline[field],
postV5: postV5[field],
});
}
}
postV5VariantComparisons.push(comparison);
}
for (const variantKey of postV5ActorByKey.keys()) {
if (!poseAuditByVariantKey.has(variantKey)) {
postV5Mismatches.push({
variantKey,
field: "variant",
baseline: null,
postV5: postV5ActorByKey.get(variantKey).variantId,
});
}
}
if (postV5Mismatches.length > 0) {
fail(
`Post-v5 summary does not exactly match the Batch A-D per-variant baseline:\n${postV5Mismatches
.slice(0, 50)
.map(
(entry) =>
` - ${entry.variantKey} ${entry.field}: baseline=${JSON.stringify(entry.baseline)} post-v5=${JSON.stringify(entry.postV5)}`,
)
.join("\n")}${postV5Mismatches.length > 50 ? `\n ... ${postV5Mismatches.length - 50} additional mismatches` : ""}`,
);
}
invariant(
postV5VariantComparisons.length === expectedTotals.runtimeUsedVariants &&
postV5VariantComparisons.every((entry) => entry.exactMatch),
"Post-v5 evidence did not produce 322 exact per-variant matches",
);
const postV5ComparisonByKey = new Map(
postV5VariantComparisons.map((entry) => [entry.variantKey, entry]),
);
const dungeonBatch = new Map();
for (const batch of batchRecords) {
for (const dungeonId of batch.dungeons) {
invariant(
!dungeonBatch.has(dungeonId),
`Dungeon ${dungeonId} appears in multiple pose batches`,
);
dungeonBatch.set(dungeonId, batch.batch);
}
}
const runtimeDungeonById = new Map(
runtimeCoverage.dungeons.map((entry) => [entry.dungeonId, entry]),
);
const dungeons = sorted(inventory.dungeons, (entry) => entry.dungeonId).map(
(inventoryDungeon) => {
const runtimeDungeon = runtimeDungeonById.get(inventoryDungeon.dungeonId);
invariant(
Boolean(runtimeDungeon),
`Runtime coverage is missing dungeon ${inventoryDungeon.dungeonId}`,
);
const batch = dungeonBatch.get(inventoryDungeon.dungeonId) ?? null;
if (inventoryDungeon.shippingAssetVariants > 0) {
invariant(
Boolean(batch),
`Configured dungeon ${inventoryDungeon.dungeonId} has no pose-audit batch`,
);
} else {
invariant(
inventoryDungeon.emptyCopyZone === true,
`Dungeon ${inventoryDungeon.dungeonId} has no variants but is not an authoritative empty copy zone`,
);
invariant(
batch === null,
`Empty copy zone ${inventoryDungeon.dungeonId} unexpectedly has pose-audit actors`,
);
}
const variantAudits = [...poseAuditByVariantKey.entries()].filter(([key]) =>
key.startsWith(`${inventoryDungeon.dungeonId}:`),
);
return {
...inventoryDungeon,
auditBatch: batch,
runtimeCoverage: runtimeDungeon,
poseAudit: {
variants: variantAudits.length,
pass: variantAudits.filter(([, audit]) => audit.status === "pass").length,
warning: variantAudits.filter(([, audit]) => audit.status === "warning")
.length,
error: variantAudits.filter(([, audit]) => audit.status === "error").length,
failed: variantAudits.filter(([, audit]) => audit.status === "failed")
.length,
flaggedSamples: variantAudits.reduce(
(total, [, audit]) => total + (audit.flaggedSamples ?? 0),
0,
),
},
};
},
);
const actorContracts = sorted(inventory.actorPackages, (entry) => entry.key);
const entityTemplates = sorted(inventory.entityTemplates, (entry) => entry.key).map(
(entry) => ({
...entry,
runtimeContract: runtimeTemplateByKey.get(entry.key),
}),
);
const runtimeUsedVariants = sorted(
inventory.shippingAssetVariants,
(entry) => entry.key,
).map((entry) => ({
...entry,
runtimeCoverage: runtimeVariantByKey.get(entry.key),
poseAudit: poseAuditByVariantKey.get(entry.key),
postV5Evidence: postV5ComparisonByKey.get(entry.key),
}));
const warningActors = runtimeUsedVariants
.filter((entry) =>
["warning", "error", "failed"].includes(entry.poseAudit.status),
)
.map((entry) => ({
key: entry.key,
variantId: entry.poseAudit.variantId,
batch: entry.poseAudit.batch,
status: entry.poseAudit.status,
warnings: entry.poseAudit.warnings,
errors: entry.poseAudit.errors,
failed: entry.poseAudit.status === "failed",
flaggedSamples: entry.poseAudit.flaggedSamples,
visualVerdict: entry.poseAudit.visualReview.visualVerdict,
disposition: entry.poseAudit.visualReview.disposition,
observations: entry.poseAudit.visualReview.observations,
}));
const sourceLimitations = {
runtimeClipCoverage: stringList(runtimeCoverage.limitations),
batches: Object.fromEntries(
batchRecords.map((entry) => [entry.batch, entry.limitations]),
),
};
const explicitLimitations = [
"The evaluated-pose audit samples the start, middle, and end of every action; a defect isolated between those samples can be missed.",
"Family contact sheets show one representative middle frame per semantic family and complement, rather than replace, all-action numeric sampling.",
"Numeric component-distance thresholds deliberately flag legitimate multipart creatures, attacks, deaths, projectiles, weapon trails, and detached effect geometry for human disposition.",
"Visual coherence does not prove original-client timing, root motion, hit timing, particles, shaders, audio, AI, encounter mechanics, or source-authentic animation choice.",
"Runtime clip coverage proves selector reachability and metadata consistency, not visual correctness; the evaluated-pose review supplies separate visual evidence.",
"The Hall of Survivors judgement-light-pose/Mantarick asset is the documented pose-only exception: it is a static posed mesh without a skeletal action set.",
"The two authoritative empty copy zones reuse another dungeon environment and contain no population templates or shipping actor variants to pose-audit.",
"A missing rebuilt animated raw GLB cache is not a missing shipping asset; manifest GLB URLs remain authoritative for runtime use.",
"Terminal Pose - dead clips are retained as forensic pose data but are not selected by the current runtime death classifier when a full Death or Drown clip exists.",
];
const poseDefects = batchRecords.flatMap((batch) =>
asArray(batch.reviewSummary.poseDefects).map((defect) => ({
batch: batch.batch,
...defect,
})),
);
const sourceComponentEvidence = batchRecords.flatMap((batch) =>
asArray(batch.reviewSummary.sourceAuthoredComponentEvidence).map((entry) => ({
batch: batch.batch,
...entry,
})),
);
const catastrophicDeformations = batchRecords.reduce((total, batch) => {
const review = batch.reviewSummary;
const count =
review.visualReview?.catastrophicDeformations ??
review.totals?.confirmedDefects ??
review.visualReview?.deformedVariants ??
review.totals?.visualErrors ??
0;
return total + count;
}, 0);
const aggregateTotals = {
...expectedTotals,
poseAuditVariants: poseAuditByVariantKey.size,
numericPassActors: runtimeUsedVariants.filter(
(entry) => entry.poseAudit.status === "pass",
).length,
numericWarningActors: runtimeUsedVariants.filter(
(entry) => entry.poseAudit.status === "warning",
).length,
numericErrorActors: runtimeUsedVariants.filter(
(entry) => entry.poseAudit.status === "error",
).length,
failedActors: runtimeUsedVariants.filter(
(entry) => entry.poseAudit.status === "failed",
).length,
evaluatedPoses: batchRecords.reduce(
(total, entry) => total + (entry.totals.poses ?? 0),
0,
),
flaggedSamples: batchRecords.reduce(
(total, entry) => total + (entry.totals.flaggedSamples ?? 0),
0,
),
warningRecords: allAuditIssues.filter(
(issue) => issue.severity === "warning",
).length,
sampleWarningRecords: allAuditIssues.filter(
(issue) => issue.severity === "warning" && issue.source === "sample",
).length,
assetWarningRecords: allAuditIssues.filter(
(issue) => issue.severity === "warning" && issue.source === "asset",
).length,
errorRecords: allAuditIssues.filter((issue) => issue.severity === "error")
.length,
warningActorsWithDisposition: warningActors.filter(
(entry) => Boolean(entry.disposition),
).length,
runtimeTemplatesMissingRequiredFamily:
runtimeCoverage.totals.templatesMissingRuntimeRequiredFamily,
runtimeVariantsMissingAnyStandardFamily:
runtimeCoverage.totals.nativeAssetsMissingAnyStandardFamily,
runtimeVariantsWithSemanticNearMiss:
runtimeCoverage.totals.nativeAssetsWithSemanticNearMiss,
inventoryFindings: inventory.findings.length,
minorPoseDefects: poseDefects.filter((entry) => entry.severity === "minor")
.length,
catastrophicDeformations,
postV5ExactVariantMatches: postV5VariantComparisons.length,
};
const status =
aggregateTotals.numericErrorActors > 0 || aggregateTotals.failedActors > 0
? "error"
: aggregateTotals.catastrophicDeformations > 0 ||
aggregateTotals.minorPoseDefects > 0
? "review"
: aggregateTotals.numericWarningActors > 0 ||
aggregateTotals.inventoryFindings > 0 ||
aggregateTotals.runtimeTemplatesMissingRequiredFamily > 0
? "pass-with-accepted-warnings"
: "pass";
const report = {
schemaVersion: 1,
reportId: "all-runewaker-actor-pose-audit",
status,
deterministic: true,
command:
"node scripts/runewaker-pipeline/generate-master-actor-pose-audit.mjs",
authority: {
inventory:
"artifacts/animation-audit/inventory.json is authoritative for dungeon, entity-template, actor-contract, shipping-variant, and empty-copy-zone inventory.",
runtime:
"artifacts/animation-audit/runtime-clip-coverage.json is authoritative for runtime asset use and selector reachability.",
numericPoseAudit:
"Each Batch A/B/C/D summary plus its per-actor pose-audit JSON reports is authoritative for evaluated numeric samples.",
visualDisposition:
"Each Batch A/B/C/D batch-*-review.json is authoritative for visual verdicts and warning dispositions.",
postV5Evidence:
"artifacts/animation-audit/post-v5/summary.json is an independent post-v5 aggregate rerun that must exactly match the Batch A-D baseline for six per-variant fields.",
},
sources: sourceFiles.map((relativePath) => {
const source = loadedSources.get(relativePath);
return { path: source.path, sha256: source.sha256 };
}),
expectedTotals,
totals: aggregateTotals,
batches: batchRecords,
emptyCopyZones: sorted(inventory.emptyCopyZones, (entry) => entry.dungeonId),
poseOnlyMapping: inventory.poseOnlyMappings,
dungeons,
actorContracts,
entityTemplates,
runtimeUsedVariants,
postV5Evidence: {
summaryPath: postV5Source.path,
summarySha256: postV5Source.sha256,
comparedFields: postV5ComparedFields,
baselineTotals,
postV5Totals: postV5Summary.totals,
exactAggregateMatch: true,
exactVariantMatches: postV5VariantComparisons.length,
mismatches: postV5Mismatches,
variants: postV5VariantComparisons,
},
visualFindings: {
catastrophicDeformations,
poseDefects,
sourceAuthoredComponentEvidence: sourceComponentEvidence,
conclusion:
catastrophicDeformations === 0 && poseDefects.length === 0
? "No quaternion-basis collapse, exploded skin, or visually confirmed pose defect was found. All numeric warnings have explicit source-topology or expected-asset dispositions."
: catastrophicDeformations === 0
? "No catastrophic quaternion-basis or exploded-skin deformation was found. Minor visual findings remain explicitly documented."
: "One or more batch reviews recorded catastrophic deformation.",
},
numericIssues: allAuditIssues,
warningActors,
inventoryFindings: sorted(
inventory.findings,
(entry) => `${entry.severity}:${entry.scope}:${entry.key}:${entry.code}`,
),
runtimeCoverageFindings: {
totals: runtimeCoverage.totals,
suspiciousClips: runtimeCoverage.suspiciousClips,
pasperGoatmanDiagnostics: runtimeCoverage.pasperGoatmanDiagnostics,
recommendedDeformationThresholds:
runtimeCoverage.recommendedDeformationThresholds,
},
explicitLimitations,
sourceLimitations,
};
function rows(values) {
return values.join("\n");
}
const batchRows = batchRecords.map(
(batch) =>
`| ${batch.batch} | ${batch.dungeons.map(markdownCode).join(", ")} | ${batch.totals.actors} | ${batch.totals.pass} | ${batch.totals.warning} | ${batch.totals.error} | ${batch.totals.failed} | ${batch.totals.poses} | ${batch.totals.flaggedSamples} | ${batch.numericWarningRecords} | ${markdownCell(batch.globalVisualVerdict)} |`,
);
const dungeonRows = dungeons.map(
(dungeon) =>
`| ${markdownCode(dungeon.dungeonId)} | ${dungeon.zoneId} | ${markdownCell(dungeon.status)} | ${markdownCell(dungeon.auditBatch)} | ${dungeon.entityTemplates} | ${dungeon.actorPackages} | ${dungeon.shippingAssetVariants} | ${dungeon.poseAudit.pass} | ${dungeon.poseAudit.warning} | ${dungeon.poseAudit.error} | ${dungeon.poseAudit.failed} | ${dungeon.poseAudit.flaggedSamples} | ${dungeon.emptyCopyZone ? `copies ${markdownCode(dungeon.copiesEnvironmentFrom)}` : "-"} |`,
);
const emptyZoneRows = report.emptyCopyZones.map(
(dungeon) =>
`| ${markdownCode(dungeon.dungeonId)} | ${dungeon.zoneId} | ${markdownCode(dungeon.copiesEnvironmentFrom)} | ${markdownCode(dungeon.recipe)} | ${markdownCell(dungeon.status)} |`,
);
const actorContractRows = actorContracts.map(
(actor) =>
`| ${markdownCode(actor.key)} | ${markdownCell(actor.animationMode)} | ${markdownCode(actor.sourceRos?.resource)} | ${actor.shippingAssetVariantKeys.length} | ${actor.templateEntityKeys.length} | ${markdownCell(actor.findingCodes.join(", "))} |`,
);
const templateRows = entityTemplates.map((template) => {
const runtime = template.runtimeContract;
return `| ${markdownCode(template.key)} | ${markdownCell(template.entityName)} | ${markdownCell(`${template.classification}/${template.runtimeKind}`)} | ${markdownCode(template.actorPackageKey)} | ${markdownCode(template.shippingAssetKey)} | ${markdownCell(template.animationMode)} | ${markdownCell(asArray(runtime.attackAnimationKinds).join(", "))} | ${markdownCell(asArray(runtime.missingRuntimeRequiredFamilies).join(", "))} |`;
});
const variantRows = runtimeUsedVariants.map((variant) => {
const runtime = variant.runtimeCoverage;
const audit = variant.poseAudit;
return `| ${markdownCode(variant.key)} | ${audit.batch} | ${markdownCell(variant.animationMode)} | ${runtime.templateIds.length} | ${markdownCode(variant.shippingGlb.url)} | ${variant.clipCount} | ${markdownCell(runtime.standardFamilyGaps.join(", "))} | ${audit.status} | ${markdownCell(audit.visualReview.visualVerdict)} | ${variant.postV5Evidence.exactMatch ? "yes" : "NO"} |`;
});
const warningActorRows = warningActors.map(
(actor) =>
`| ${markdownCode(actor.variantId)} | ${actor.batch} | ${actor.status} | ${actor.warnings} | ${actor.errors} | ${actor.flaggedSamples} | ${markdownCell(actor.visualVerdict)} | ${markdownCell(actor.disposition)} |`,
);
const issueRows = allAuditIssues.map(
(issue) =>
`| ${issue.batch} | ${markdownCode(issue.variantId)} | ${markdownCell(issue.source)} | ${markdownCell(issue.severity)} | ${markdownCode(issue.code)} | ${markdownCell(issue.action)} | ${markdownCell(issue.frameLabel)} | ${markdownCell(issue.frame)} | ${markdownCell(issue.value)} | ${markdownCell(issue.detail)} |`,
);
const inventoryFindingRows = report.inventoryFindings.map(
(finding) =>
`| ${markdownCell(finding.severity)} | ${markdownCell(finding.scope)} | ${markdownCode(finding.key)} | ${markdownCode(finding.code)} | ${markdownCell(finding.message)} |`,
);
const suspiciousClipCounts = Object.entries(
asArray(runtimeCoverage.suspiciousClips).reduce((counts, entry) => {
counts[entry.code] = (counts[entry.code] ?? 0) + 1;
return counts;
}, {}),
).sort(([left], [right]) => left.localeCompare(right, "en"));
const poseDefectRows = poseDefects.map(
(defect) =>
`| ${defect.batch} | ${markdownCode(defect.variant)} | ${markdownCell(defect.severity)} | ${markdownCell(defect.sample)} | ${markdownCode(defect.component)} | ${markdownCell(defect.vertices)} | ${markdownCell(defect.runtimeExposure)} | ${markdownCell(defect.disposition)} |`,
);
const sourceComponentEvidenceRows = sourceComponentEvidence.map(
(entry) =>
`| ${entry.batch} | ${markdownCode(entry.variant)} | ${markdownCell(entry.sample)} | ${markdownCode(entry.component)} | ${markdownCell(entry.vertices)} | ${markdownCell(entry.faces)} | ${markdownCode(entry.primaryBone)} | ${markdownCell(entry.shapeDiagonalRatio)} | ${markdownCell(entry.componentsSharingPrimaryBone)} | ${markdownCell(entry.repairDecision)} |`,
);
const markdown = `# All RuneWaker actor pose audit
## Status
**${status.toUpperCase()}**. This deterministic report merges the authoritative actor inventory, runtime clip-selector coverage, four evaluated-pose batch summaries, every per-actor numeric warning/error, and every batch visual disposition.
The inventory is fully enumerated: **${aggregateTotals.dungeons} dungeons**, **${aggregateTotals.entityTemplates} entity templates**, **${aggregateTotals.actorContracts} actor contracts**, and **${aggregateTotals.runtimeUsedVariants} runtime-used shipping variants**. The two environment-copy zones are explicitly empty and therefore have no actor variants to audit.
The visual reviews found **${aggregateTotals.catastrophicDeformations} catastrophic deformations** and **${aggregateTotals.minorPoseDefects} confirmed minor pose defects**. Every numeric warning remains in the report with an explicit visual or source-topology disposition.
## Rebuild command
\`\`\`powershell
node scripts/runewaker-pipeline/generate-master-actor-pose-audit.mjs
\`\`\`
The generator fails before writing either output if any Batch A/B/C/D summary or review is missing, if batch variants do not exactly equal the 322 runtime-used variants, or if a warning/error actor lacks an explicit visual disposition.
## Aggregate totals
| Measure | Count |
| --- | ---: |
| Dungeons | ${aggregateTotals.dungeons} |
| Entity templates | ${aggregateTotals.entityTemplates} |
| Actor contracts | ${aggregateTotals.actorContracts} |
| Runtime-used variants | ${aggregateTotals.runtimeUsedVariants} |
| Empty copy zones | ${aggregateTotals.emptyCopyZones} |
| Numeric pass actors | ${aggregateTotals.numericPassActors} |
| Numeric warning actors | ${aggregateTotals.numericWarningActors} |
| Numeric error actors | ${aggregateTotals.numericErrorActors} |
| Failed actor subprocesses | ${aggregateTotals.failedActors} |
| Evaluated poses | ${aggregateTotals.evaluatedPoses} |
| Flagged samples | ${aggregateTotals.flaggedSamples} |
| Warning records | ${aggregateTotals.warningRecords} |
| Sample warning records | ${aggregateTotals.sampleWarningRecords} |
| Asset warning records | ${aggregateTotals.assetWarningRecords} |
| Error records | ${aggregateTotals.errorRecords} |
| Warning/error actors with dispositions | ${aggregateTotals.warningActorsWithDisposition} |
| Templates missing a runtime-required family | ${aggregateTotals.runtimeTemplatesMissingRequiredFamily} |
| Runtime variants missing any standard family | ${aggregateTotals.runtimeVariantsMissingAnyStandardFamily} |
| Runtime variants with semantic near-misses | ${aggregateTotals.runtimeVariantsWithSemanticNearMiss} |
| Inventory findings | ${aggregateTotals.inventoryFindings} |
| Minor pose defects retained | ${aggregateTotals.minorPoseDefects} |
| Catastrophic deformations | ${aggregateTotals.catastrophicDeformations} |
| Post-v5 exact per-variant matches | ${aggregateTotals.postV5ExactVariantMatches} |
The 635 warning records comprise the 634 flagged pose samples plus the documented Hall of Survivors \`judgement-light-pose\` / Mantarick \`pose-only-no-armature\` asset warning.
## Batch coverage
| Batch | Dungeons | Variants | Pass | Warning | Error | Failed | Poses | Flagged samples | Warning records | Visual verdict |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |
${rows(batchRows)}
## Independent post-v5 evidence
The post-v5 rerun is an exact aggregate and per-variant match to the four reviewed batches. The generator compares \`status\`, \`actions\`, \`poses\`, \`flaggedSamples\`, \`errors\`, and \`warnings\` for every shipping variant and fails before writing if any value differs.
| Evidence | Batch A-D baseline | Post-v5 rerun |
| --- | ---: | ---: |
| Actors | ${baselineTotals.actors} | ${postV5Summary.totals.actors} |
| Pass | ${baselineTotals.pass} | ${postV5Summary.totals.pass} |
| Warning | ${baselineTotals.warning} | ${postV5Summary.totals.warning} |
| Error | ${baselineTotals.error} | ${postV5Summary.totals.error} |
| Failed | ${baselineTotals.failed} | ${postV5Summary.totals.failed} |
| Evaluated poses | ${baselineTotals.poses} | ${postV5Summary.totals.poses} |
| Flagged samples | ${baselineTotals.flaggedSamples} | ${postV5Summary.totals.flaggedSamples} |
| Exact per-variant comparisons | ${postV5VariantComparisons.length} | ${postV5VariantComparisons.length} |
Post-v5 summary SHA-256: ${markdownCode(postV5Source.sha256)}.
## All 30 dungeons
| Dungeon | Zone | Inventory status | Pose batch | Templates | Actor contracts | Runtime variants | Pass | Warning | Error | Failed | Flagged samples | Empty-copy detail |
| --- | ---: | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |
${rows(dungeonRows)}
## Authoritative empty copy zones
| Dungeon | Zone | Environment source | Recipe | Status |
| --- | ---: | --- | --- | --- |
${rows(emptyZoneRows)}
These zones copy dungeon environment content but have zero configured entity templates, actor contracts, manifest variants, and runtime-used shipping variants. Their absence from the Blender pose batches is intentional.
## All 268 actor contracts
| Actor contract | Mode | Source ROS | Runtime variants | Entity templates | Inventory findings |
| --- | --- | --- | ---: | ---: | --- |
${rows(actorContractRows)}
## All 436 entity templates
| Entity template | Name | Classification/kind | Actor contract | Runtime asset | Mode | Attack kinds | Missing runtime families |
| --- | --- | --- | --- | --- | --- | --- | --- |
${rows(templateRows)}
## All 322 runtime-used variants
| Runtime variant | Batch | Mode | Templates | Shipping GLB | Clips | Standard-family gaps | Numeric verdict | Visual verdict | Post-v5 exact match |
| --- | --- | --- | ---: | --- | ---: | --- | --- | --- | --- |
${rows(variantRows)}
## Visual defect review
| Batch | Variant | Severity | Sample | Component | Vertices | Runtime exposure | Disposition |
| --- | --- | --- | --- | --- | ---: | --- | --- |
${poseDefectRows.length > 0 ? rows(poseDefectRows) : "| - | - | - | - | - | - | - | None |"}
${poseDefectRows.length > 0 ? "No catastrophic quaternion-basis deformation or exploded skin was found; the confirmed minor findings above remain explicitly documented." : "No catastrophic quaternion-basis deformation, exploded skin, or confirmed minor pose defect was found."}
## Source-authored component evidence
These components crossed the conservative distance threshold but retained their source topology, bone assignments, and shape. The repair decision is therefore to preserve them rather than delete or reweight authentic geometry.
| Batch | Variant | Sample | Component | Vertices | Faces | Primary bone | Shape ratio | Sibling components | Decision |
| --- | --- | --- | --- | ---: | ---: | --- | ---: | ---: | --- |
${sourceComponentEvidenceRows.length > 0 ? rows(sourceComponentEvidenceRows) : "| - | - | - | - | - | - | - | - | - | None |"}
## Numeric warning/error actor dispositions
| Variant | Batch | Numeric status | Warning records | Error records | Flagged samples | Visual verdict | Disposition |
| --- | --- | --- | ---: | ---: | ---: | --- | --- |
${rows(warningActorRows)}
## Every numeric warning/error record
| Batch | Variant | Source | Severity | Code | Action | Frame | Time/frame | Value | Detail |
| --- | --- | --- | --- | --- | --- | --- | ---: | ---: | --- |
${rows(issueRows)}
Each sample warning above remains traceable through the variant's \`poseAudit.report\` field in the JSON report, where the authoritative named component outliers are retained.
## Inventory findings
| Severity | Scope | Key | Code | Message |
| --- | --- | --- | --- | --- |
${rows(inventoryFindingRows)}
Raw-cache warnings do not mean the packaged GLB is absent. The shipping manifest path remains the runtime authority.
## Runtime-selector findings
${suspiciousClipCounts.length > 0 ? suspiciousClipCounts.map(([code, count]) => `- ${markdownCode(code)}: ${count}`).join("\n") : "- None."}
The variant table above enumerates all standard-family gaps. The JSON report also retains the runtime audit's full suspicious-clip array, Pasper goatman diagnostics, and recommended deformation thresholds.
## Explicit limitations
${explicitLimitations.map((entry) => `- ${entry}`).join("\n")}
## Source-specific limitations
### Runtime clip coverage
${sourceLimitations.runtimeClipCoverage.map((entry) => `- ${entry}`).join("\n")}
${batchRecords
.map(
(batch) => `### ${batch.batch}\n\n${batch.limitations.length > 0 ? batch.limitations.map((entry) => `- ${entry}`).join("\n") : "- No additional batch-specific limitations were recorded."}`,
)
.join("\n\n")}
## Input provenance
| Input | SHA-256 |
| --- | --- |
${sourceFiles
.map((relativePath) => {
const source = loadedSources.get(relativePath);
return `| ${markdownCode(source.path)} | ${markdownCode(source.sha256)} |`;
})
.join("\n")}
`;
await writeFile(outputJsonPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
await writeFile(outputMarkdownPath, markdown, "utf8");
console.log(`Wrote ${toProjectPath(outputJsonPath)}`);
console.log(`Wrote ${toProjectPath(outputMarkdownPath)}`);
console.log(
`dungeons=${aggregateTotals.dungeons} templates=${aggregateTotals.entityTemplates} actorContracts=${aggregateTotals.actorContracts} runtimeVariants=${aggregateTotals.runtimeUsedVariants} warningRecords=${aggregateTotals.warningRecords} errorRecords=${aggregateTotals.errorRecords}`,
);