create-aura3d 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/dist/cli.js +10 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +11 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/showcase-spec-artifacts.d.ts +104 -0
  8. package/dist/showcase-spec-artifacts.d.ts.map +1 -0
  9. package/dist/showcase-spec-artifacts.js +142 -0
  10. package/dist/showcase-spec-artifacts.js.map +1 -0
  11. package/dist/showcase-spec-category-plan.d.ts +15 -0
  12. package/dist/showcase-spec-category-plan.d.ts.map +1 -0
  13. package/dist/showcase-spec-category-plan.js +45 -0
  14. package/dist/showcase-spec-category-plan.js.map +1 -0
  15. package/dist/showcase-spec-compiler.d.ts +5 -0
  16. package/dist/showcase-spec-compiler.d.ts.map +1 -0
  17. package/dist/showcase-spec-compiler.js +240 -0
  18. package/dist/showcase-spec-compiler.js.map +1 -0
  19. package/dist/showcase-spec-evidence.d.ts +7 -0
  20. package/dist/showcase-spec-evidence.d.ts.map +1 -0
  21. package/dist/showcase-spec-evidence.js +815 -0
  22. package/dist/showcase-spec-evidence.js.map +1 -0
  23. package/dist/showcase-spec-game-geometry-extractor.d.ts +21 -0
  24. package/dist/showcase-spec-game-geometry-extractor.d.ts.map +1 -0
  25. package/dist/showcase-spec-game-geometry-extractor.js +725 -0
  26. package/dist/showcase-spec-game-geometry-extractor.js.map +1 -0
  27. package/dist/showcase-spec-game-replacement-evidence.d.ts +14 -0
  28. package/dist/showcase-spec-game-replacement-evidence.d.ts.map +1 -0
  29. package/dist/showcase-spec-game-replacement-evidence.js +219 -0
  30. package/dist/showcase-spec-game-replacement-evidence.js.map +1 -0
  31. package/dist/showcase-spec-game-template-evidence.d.ts +72 -0
  32. package/dist/showcase-spec-game-template-evidence.d.ts.map +1 -0
  33. package/dist/showcase-spec-game-template-evidence.js +435 -0
  34. package/dist/showcase-spec-game-template-evidence.js.map +1 -0
  35. package/dist/showcase-spec-gameplay-evidence.d.ts +3 -0
  36. package/dist/showcase-spec-gameplay-evidence.d.ts.map +1 -0
  37. package/dist/showcase-spec-gameplay-evidence.js +108 -0
  38. package/dist/showcase-spec-gameplay-evidence.js.map +1 -0
  39. package/dist/showcase-spec-parser.d.ts +3 -0
  40. package/dist/showcase-spec-parser.d.ts.map +1 -0
  41. package/dist/showcase-spec-parser.js +662 -0
  42. package/dist/showcase-spec-parser.js.map +1 -0
  43. package/dist/showcase-spec-platformer-artifacts.d.ts +3 -0
  44. package/dist/showcase-spec-platformer-artifacts.d.ts.map +1 -0
  45. package/dist/showcase-spec-platformer-artifacts.js +276 -0
  46. package/dist/showcase-spec-platformer-artifacts.js.map +1 -0
  47. package/dist/showcase-spec-racing-artifacts.d.ts +3 -0
  48. package/dist/showcase-spec-racing-artifacts.d.ts.map +1 -0
  49. package/dist/showcase-spec-racing-artifacts.js +273 -0
  50. package/dist/showcase-spec-racing-artifacts.js.map +1 -0
  51. package/dist/showcase-spec-replacement-apply.d.ts +10 -0
  52. package/dist/showcase-spec-replacement-apply.d.ts.map +1 -0
  53. package/dist/showcase-spec-replacement-apply.js +166 -0
  54. package/dist/showcase-spec-replacement-apply.js.map +1 -0
  55. package/dist/showcase-spec-replacement-candidates.d.ts +16 -0
  56. package/dist/showcase-spec-replacement-candidates.d.ts.map +1 -0
  57. package/dist/showcase-spec-replacement-candidates.js +244 -0
  58. package/dist/showcase-spec-replacement-candidates.js.map +1 -0
  59. package/dist/showcase-spec-replacement-game-geometry.d.ts +4 -0
  60. package/dist/showcase-spec-replacement-game-geometry.d.ts.map +1 -0
  61. package/dist/showcase-spec-replacement-game-geometry.js +175 -0
  62. package/dist/showcase-spec-replacement-game-geometry.js.map +1 -0
  63. package/dist/showcase-spec-replacement-manifest.d.ts +41 -0
  64. package/dist/showcase-spec-replacement-manifest.d.ts.map +1 -0
  65. package/dist/showcase-spec-replacement-manifest.js +62 -0
  66. package/dist/showcase-spec-replacement-manifest.js.map +1 -0
  67. package/dist/showcase-spec-replacement-values.d.ts +9 -0
  68. package/dist/showcase-spec-replacement-values.d.ts.map +1 -0
  69. package/dist/showcase-spec-replacement-values.js +41 -0
  70. package/dist/showcase-spec-replacement-values.js.map +1 -0
  71. package/dist/showcase-spec-replacement.d.ts +13 -0
  72. package/dist/showcase-spec-replacement.d.ts.map +1 -0
  73. package/dist/showcase-spec-replacement.js +144 -0
  74. package/dist/showcase-spec-replacement.js.map +1 -0
  75. package/dist/showcase-spec-types.d.ts +304 -0
  76. package/dist/showcase-spec-types.d.ts.map +1 -0
  77. package/dist/showcase-spec-types.js +2 -0
  78. package/dist/showcase-spec-types.js.map +1 -0
  79. package/package.json +21 -2
  80. package/templates/animation-channel/package.json +1 -1
  81. package/templates/animation-channel/tests/screenshot.spec.ts +16 -0
  82. package/templates/animation-studio/index.html +12 -0
  83. package/templates/animation-studio/node_modules.publish-aside/.vite/deps/_metadata.json +8 -0
  84. package/templates/animation-studio/node_modules.publish-aside/.vite/deps/package.json +3 -0
  85. package/templates/animation-studio/package.json +1 -1
  86. package/templates/animation-studio/scripts/animation-scene.ts +1 -1
  87. package/templates/animation-studio/scripts/asset-motion-probe.ts +7 -1
  88. package/templates/animation-studio/scripts/author-office-scene.ts +1 -1
  89. package/templates/animation-studio/scripts/build-characters.ts +1 -1
  90. package/templates/animation-studio/scripts/render-live.ts +24 -0
  91. package/templates/animation-studio/src/director/director-heuristics.ts +2 -2
  92. package/templates/animation-studio/src/director/{prompt-to-scene.ts → prompt-scene.ts} +74 -6
  93. package/templates/animation-studio/studio/src/App.tsx +146 -78
  94. package/templates/animation-studio/studio/src/components/Console.tsx +8 -2
  95. package/templates/animation-studio/studio/src/components/Inspector.tsx +57 -1
  96. package/templates/animation-studio/studio/src/components/Stage.tsx +10 -5
  97. package/templates/animation-studio/studio/src/components/Timeline.tsx +49 -2
  98. package/templates/animation-studio/studio/src/state/mapDocument.ts +13 -2
  99. package/templates/animation-studio/studio/src/state/sceneTool.ts +20 -9
  100. package/templates/animation-studio/studio/src/state/types.ts +8 -0
  101. package/templates/animation-studio/tests/route-health.spec.ts +33 -0
  102. package/templates/animation-studio/tests/screenshot.spec.ts +30 -0
  103. package/templates/character-controller/package.json +1 -1
  104. package/templates/character-controller/src/main.ts +18 -1
  105. package/templates/cinematic-scene/package.json +1 -1
  106. package/templates/cinematic-scene/tests/screenshot.spec.ts +3 -3
  107. package/templates/episode-builder/package.json +1 -1
  108. package/templates/episode-builder/tests/screenshot.spec.ts +15 -0
  109. package/templates/falling-blocks-starter/README.md +18 -0
  110. package/templates/falling-blocks-starter/index.html +23 -0
  111. package/templates/falling-blocks-starter/package.json +21 -0
  112. package/templates/falling-blocks-starter/playwright.config.ts +12 -0
  113. package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.679d52fe.glb +0 -0
  114. package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.thumb.svg +1 -0
  115. package/templates/falling-blocks-starter/src/aura-assets.ts +20 -0
  116. package/templates/falling-blocks-starter/src/main.ts +358 -0
  117. package/templates/falling-blocks-starter/tests/playable.spec.ts +85 -0
  118. package/templates/falling-blocks-starter/tests/route-health.spec.ts +34 -0
  119. package/templates/falling-blocks-starter/tsconfig.json +13 -0
  120. package/templates/fighting-game/package.json +1 -1
  121. package/templates/fighting-game/tests/screenshot.spec.ts +15 -0
  122. package/templates/mini-game/README.md +13 -0
  123. package/templates/mini-game/index.html +1 -1
  124. package/templates/mini-game/package.json +2 -2
  125. package/templates/mini-game/src/main.ts +251 -21
  126. package/templates/mini-game/tests/playable.spec.ts +49 -0
  127. package/templates/mini-game/tests/screenshot.spec.ts +37 -32
  128. package/templates/product-viewer/package.json +1 -1
  129. package/templates/product-viewer/tests/screenshot.spec.ts +4 -4
  130. package/templates/prompt-animation-channel/package.json +1 -1
  131. package/templates/prompt-animation-channel/tests/screenshot.spec.ts +15 -0
  132. package/templates/racing-starter/README.md +27 -0
  133. package/templates/racing-starter/index.html +23 -0
  134. package/templates/racing-starter/package.json +21 -0
  135. package/templates/racing-starter/playwright.config.ts +12 -0
  136. package/templates/racing-starter/public/aura-assets/showcaseCleanSportsCar.1c69283f.glb +0 -0
  137. package/templates/racing-starter/public/aura-assets/showcaseCleanSportsCar.thumb.svg +1 -0
  138. package/templates/racing-starter/public/aura-assets/showcaseReadableKartCircuit.5cbb912e.glb +0 -0
  139. package/templates/racing-starter/public/aura-assets/showcaseReadableKartCircuit.thumb.svg +1 -0
  140. package/templates/racing-starter/src/aura-assets.ts +36 -0
  141. package/templates/racing-starter/src/main.ts +280 -0
  142. package/templates/racing-starter/tests/playable.spec.ts +93 -0
  143. package/templates/racing-starter/tests/route-health.spec.ts +40 -0
  144. package/templates/racing-starter/tsconfig.json +13 -0
  145. package/templates/three-compat-architecture-interior/index.html +16 -0
  146. package/templates/three-compat-architecture-interior/package.json +21 -0
  147. package/templates/three-compat-architecture-interior/playwright.config.ts +14 -0
  148. package/templates/three-compat-architecture-interior/src/main.ts +27 -0
  149. package/templates/three-compat-architecture-interior/tests/route-health.spec.ts +16 -0
  150. package/templates/three-compat-architecture-interior/tests/screenshot.spec.ts +15 -0
  151. package/templates/three-compat-architecture-interior/tsconfig.json +19 -0
  152. package/templates/three-compat-asset-inspector/index.html +16 -0
  153. package/templates/three-compat-asset-inspector/package.json +21 -0
  154. package/templates/three-compat-asset-inspector/playwright.config.ts +14 -0
  155. package/templates/three-compat-asset-inspector/src/main.ts +17 -0
  156. package/templates/three-compat-asset-inspector/tests/route-health.spec.ts +16 -0
  157. package/templates/three-compat-asset-inspector/tests/screenshot.spec.ts +15 -0
  158. package/templates/three-compat-asset-inspector/tsconfig.json +19 -0
  159. package/templates/three-compat-character-viewer/index.html +16 -0
  160. package/templates/three-compat-character-viewer/package.json +21 -0
  161. package/templates/three-compat-character-viewer/playwright.config.ts +14 -0
  162. package/templates/three-compat-character-viewer/src/main.ts +17 -0
  163. package/templates/three-compat-character-viewer/tests/route-health.spec.ts +16 -0
  164. package/templates/three-compat-character-viewer/tests/screenshot.spec.ts +15 -0
  165. package/templates/three-compat-character-viewer/tsconfig.json +19 -0
  166. package/templates/three-compat-custom-threejs-migration/index.html +16 -0
  167. package/templates/three-compat-custom-threejs-migration/package.json +21 -0
  168. package/templates/three-compat-custom-threejs-migration/playwright.config.ts +14 -0
  169. package/templates/three-compat-custom-threejs-migration/src/main.ts +16 -0
  170. package/templates/three-compat-custom-threejs-migration/tests/route-health.spec.ts +16 -0
  171. package/templates/three-compat-custom-threejs-migration/tests/screenshot.spec.ts +15 -0
  172. package/templates/three-compat-custom-threejs-migration/tsconfig.json +19 -0
  173. package/templates/three-compat-large-scene/index.html +16 -0
  174. package/templates/three-compat-large-scene/package.json +21 -0
  175. package/templates/three-compat-large-scene/playwright.config.ts +14 -0
  176. package/templates/three-compat-large-scene/src/main.ts +31 -0
  177. package/templates/three-compat-large-scene/tests/route-health.spec.ts +16 -0
  178. package/templates/three-compat-large-scene/tests/screenshot.spec.ts +15 -0
  179. package/templates/three-compat-large-scene/tsconfig.json +19 -0
  180. package/templates/three-compat-material-authoring/index.html +16 -0
  181. package/templates/three-compat-material-authoring/package.json +21 -0
  182. package/templates/three-compat-material-authoring/playwright.config.ts +14 -0
  183. package/templates/three-compat-material-authoring/src/main.ts +33 -0
  184. package/templates/three-compat-material-authoring/tests/route-health.spec.ts +16 -0
  185. package/templates/three-compat-material-authoring/tests/screenshot.spec.ts +15 -0
  186. package/templates/three-compat-material-authoring/tsconfig.json +19 -0
  187. package/templates/three-compat-postprocess-scene/index.html +16 -0
  188. package/templates/three-compat-postprocess-scene/package.json +21 -0
  189. package/templates/three-compat-postprocess-scene/playwright.config.ts +14 -0
  190. package/templates/three-compat-postprocess-scene/src/main.ts +17 -0
  191. package/templates/three-compat-postprocess-scene/tests/route-health.spec.ts +16 -0
  192. package/templates/three-compat-postprocess-scene/tests/screenshot.spec.ts +15 -0
  193. package/templates/three-compat-postprocess-scene/tsconfig.json +19 -0
  194. package/templates/three-compat-premium-product-viewer/index.html +16 -0
  195. package/templates/three-compat-premium-product-viewer/package.json +21 -0
  196. package/templates/three-compat-premium-product-viewer/playwright.config.ts +14 -0
  197. package/templates/three-compat-premium-product-viewer/src/main.ts +18 -0
  198. package/templates/three-compat-premium-product-viewer/tests/route-health.spec.ts +16 -0
  199. package/templates/three-compat-premium-product-viewer/tests/screenshot.spec.ts +15 -0
  200. package/templates/three-compat-premium-product-viewer/tsconfig.json +19 -0
  201. package/templates/animation-channel/public/aura-assets/moon-garden-feature-frame.png +0 -0
  202. package/templates/animation-channel/tests/reports/prompt-animation/cartoon-sample-episode.png +0 -0
  203. package/templates/animation-studio/dist/episodes/scene/episode-3d.webm +0 -0
  204. package/templates/animation-studio/dist/episodes/scene/frames/action.png +0 -0
  205. package/templates/animation-studio/dist/episodes/scene/frames/dialogue.png +0 -0
  206. package/templates/animation-studio/dist/episodes/scene/frames/final.png +0 -0
  207. package/templates/animation-studio/dist/episodes/scene/frames/first.png +0 -0
  208. package/templates/animation-studio/dist/episodes/scene/frames/mouth-closed.png +0 -0
  209. package/templates/animation-studio/dist/episodes/scene/frames/mouth-open.png +0 -0
  210. package/templates/animation-studio/dist/episodes/scene/render-live-summary.json +0 -5616
  211. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/broken.png +0 -0
  212. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/chefs.png +0 -0
  213. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/customer.png +0 -0
  214. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-1.png +0 -0
  215. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-2.png +0 -0
  216. package/templates/animation-studio/dist/generated/second-scene.document.json +0 -1210
  217. package/templates/animation-studio/dist/generated/studio-edited.document.json +0 -1196
  218. package/templates/animation-studio/dist/scene/inshader-accept.document.json +0 -1
  219. package/templates/animation-studio/dist/scene/inshader-test.document.json +0 -1
  220. package/templates/animation-studio/dist/scene/integ.document.json +0 -1
  221. package/templates/animation-studio/dist/scene/working.document.json +0 -594
  222. package/templates/animation-studio/dist/scene/working.history.json +0 -1
  223. package/templates/animation-studio/studio/dist/assets/index-i8URxOOt.css +0 -1
  224. package/templates/animation-studio/studio/dist/assets/index-pC9hdZ-B.js +0 -11
  225. package/templates/animation-studio/studio/dist/index.html +0 -19
  226. package/templates/animation-studio/tests/reports/animation-studio/hifi-cast-validation.json +0 -938
@@ -0,0 +1,815 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, readFileSync, statSync } from "node:fs";
3
+ import { isAbsolute, join } from "node:path";
4
+ import { extractPlatformerPlayableSurfaceMapFromAsset, extractRacingTrackTopologyFromAsset } from "./showcase-spec-game-geometry-extractor.js";
5
+ import { validateGameGameplayProof } from "./showcase-spec-gameplay-evidence.js";
6
+ const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
7
+ export function compileEvidenceBlockers(spec, context = {}) {
8
+ if (spec.publicStatus !== "release-ready candidate")
9
+ return [];
10
+ const blockers = [];
11
+ blockers.push(...validateEvidenceFile("evidence:route-primary-screenshot", spec.evidence.routePrimaryScreenshot, context));
12
+ blockers.push(...validateRoutePrimaryProbe(spec));
13
+ blockers.push(...validateGameplayProof(spec));
14
+ blockers.push(...validateReleaseAssetProbes(spec));
15
+ blockers.push(...validateDeployEvidence(spec));
16
+ blockers.push(...validateCategoryTemplateEvidence(spec, context));
17
+ for (const capability of spec.capabilities) {
18
+ if (capability.evidence)
19
+ blockers.push(...validateEvidenceFile(`capability:${capability.name}:evidence`, capability.evidence, context));
20
+ }
21
+ return blockers;
22
+ }
23
+ function validateCategoryTemplateEvidence(spec, context) {
24
+ if (spec.category === "game-racing") {
25
+ const evidencePath = spec.racing?.raceDesign.trackTopologyEvidence;
26
+ const blockers = [];
27
+ if (!evidencePath) {
28
+ blockers.push("evidence:racing-track-topology:missing");
29
+ }
30
+ else {
31
+ const parsed = readJsonFile(evidencePath, "evidence:racing-track-topology", context);
32
+ blockers.push(...(parsed.ok ? validateRacingTrackTopologyEvidence(spec, parsed.value, context) : parsed.blockers));
33
+ }
34
+ blockers.push(...validateGameAssetPairEvidence({
35
+ label: "racing-asset-pair",
36
+ spec,
37
+ evidence: spec.racing?.raceDesign.assetPairEvidence,
38
+ expectedCategory: "racing",
39
+ expectedAssets: spec.racing ? [spec.racing.vehicleAsset, spec.racing.trackAsset] : [],
40
+ context
41
+ }));
42
+ return blockers;
43
+ }
44
+ if (spec.category === "game-platformer") {
45
+ const evidencePath = spec.platformer?.levelDesign.playableSurfaceEvidence;
46
+ const blockers = [];
47
+ if (!evidencePath) {
48
+ blockers.push("evidence:platformer-playable-surface:missing");
49
+ }
50
+ else {
51
+ const parsed = readJsonFile(evidencePath, "evidence:platformer-playable-surface", context);
52
+ blockers.push(...(parsed.ok ? validatePlatformerPlayableSurfaceEvidence(spec, parsed.value, context) : parsed.blockers));
53
+ }
54
+ blockers.push(...validateGameAssetPairEvidence({
55
+ label: "platformer-asset-pair",
56
+ spec,
57
+ evidence: spec.platformer?.levelDesign.assetPairEvidence,
58
+ expectedCategory: "platformer",
59
+ expectedAssets: spec.platformer ? [spec.platformer.characterAsset, ...spec.platformer.worldAssets] : [],
60
+ context
61
+ }));
62
+ return blockers;
63
+ }
64
+ return [];
65
+ }
66
+ function validateGameplayProof(spec) {
67
+ if (!isGameSpec(spec))
68
+ return [];
69
+ if (!spec.evidence.gameplayProof)
70
+ return ["evidence:gameplay-proof:missing"];
71
+ const parsed = readJsonFile(spec.evidence.gameplayProof, "evidence:gameplay-proof");
72
+ if (!parsed.ok)
73
+ return parsed.blockers;
74
+ if (!isRecord(parsed.value))
75
+ return ["evidence:gameplay-proof:invalid-json"];
76
+ return [
77
+ ...(parsed.value.pass === true ? [] : ["evidence:gameplay-proof:not-passing"]),
78
+ ...validateGameGameplayProof(spec, parsed.value)
79
+ ];
80
+ }
81
+ function validateReleaseAssetProbes(spec) {
82
+ if (!isGameSpec(spec))
83
+ return [];
84
+ const blockers = [];
85
+ const releaseAssetProbes = spec.evidence.releaseAssetProbes;
86
+ for (const asset of spec.primaryAssets) {
87
+ const probePath = releaseAssetProbes?.[asset.id];
88
+ if (!probePath) {
89
+ blockers.push(`evidence:release-asset-probe:missing:${asset.id}`);
90
+ continue;
91
+ }
92
+ const parsed = readJsonFile(probePath, `evidence:release-asset-probe:${asset.id}`);
93
+ if (!parsed.ok) {
94
+ blockers.push(...parsed.blockers);
95
+ continue;
96
+ }
97
+ blockers.push(...validateReleaseAssetProbe(asset.id, parsed.value));
98
+ }
99
+ return blockers;
100
+ }
101
+ function isGameSpec(spec) {
102
+ return spec.category === "game-platformer" || spec.category === "game-racing";
103
+ }
104
+ function validateReleaseAssetProbe(assetId, value) {
105
+ if (!isRecord(value))
106
+ return [`evidence:release-asset-probe:invalid-json:${assetId}`];
107
+ const evidence = value.evidence;
108
+ if (!isRecord(evidence))
109
+ return [`evidence:release-asset-probe:missing-evidence:${assetId}`];
110
+ const asset = evidence.asset;
111
+ const blockers = [];
112
+ if (!isRecord(asset) || asset.id !== assetId)
113
+ blockers.push(`evidence:release-asset-probe:asset-mismatch:${assetId}`);
114
+ if (evidence.pass !== true)
115
+ blockers.push(`evidence:release-asset-probe:not-passing:${assetId}`);
116
+ if (Array.isArray(evidence.failures) && evidence.failures.length > 0)
117
+ blockers.push(`evidence:release-asset-probe:has-failures:${assetId}`);
118
+ const renderedProbe = value.renderedProbe;
119
+ if (!isRecord(renderedProbe)) {
120
+ blockers.push(`evidence:release-asset-probe:missing-rendered-probe:${assetId}`);
121
+ }
122
+ else {
123
+ if (typeof renderedProbe.sha256 !== "string" || !renderedProbe.sha256.startsWith("sha256-")) {
124
+ blockers.push(`evidence:release-asset-probe:missing-sha256:${assetId}`);
125
+ }
126
+ if (!isRecord(renderedProbe.foregroundBounds))
127
+ blockers.push(`evidence:release-asset-probe:missing-foreground-bounds:${assetId}`);
128
+ }
129
+ return blockers;
130
+ }
131
+ function validateRoutePrimaryProbe(spec) {
132
+ const parsed = readJsonFile(spec.evidence.routePrimaryProbe, "evidence:route-primary-probe");
133
+ if (!parsed.ok)
134
+ return parsed.blockers;
135
+ if (!isRecord(parsed.value))
136
+ return ["evidence:route-primary-probe:invalid-json"];
137
+ const blockers = [];
138
+ if (parsed.value.pass !== true)
139
+ blockers.push("evidence:route-primary-probe:not-passing");
140
+ if (parsed.value.routePrimaryHeroAsset !== spec.layout.heroAsset)
141
+ blockers.push("evidence:route-primary-probe:hero-mismatch");
142
+ if (!routePrimaryAssetsInclude(parsed.value, spec.layout.heroAsset))
143
+ blockers.push("evidence:route-primary-probe:missing-hero-asset");
144
+ if (!routePrimaryScreenshotMatches(parsed.value, spec.evidence.routePrimaryScreenshot)) {
145
+ blockers.push("evidence:route-primary-probe:screenshot-mismatch");
146
+ }
147
+ return blockers;
148
+ }
149
+ function validateDeployEvidence(spec) {
150
+ const blockers = [];
151
+ if (!isReleaseDeployCommand(spec))
152
+ blockers.push("evidence:deploy-command:not-release-check-deploy");
153
+ if (!spec.evidence.deployEvidence)
154
+ return [...blockers, "evidence:deploy-artifact:missing"];
155
+ const parsed = readJsonFile(spec.evidence.deployEvidence, "evidence:deploy-artifact");
156
+ if (!parsed.ok)
157
+ return [...blockers, ...parsed.blockers];
158
+ return [...blockers, ...validateDeployReport(spec, parsed.value)];
159
+ }
160
+ function isReleaseDeployCommand(spec) {
161
+ const command = spec.evidence.deployCommand;
162
+ return command.startsWith("pnpm exec tsx --tsconfig tsconfig.base.json packages/aura3d-cli/src/cli.ts check-deploy ")
163
+ && command.includes(" --release")
164
+ && command.includes(" --dist ")
165
+ && command.includes(" --source ")
166
+ && spec.primaryAssets.every((asset) => command.includes(` --asset ${asset.id}`));
167
+ }
168
+ function validateEvidenceFile(label, filePath, context = {}) {
169
+ if (!isSafeRelativePath(filePath))
170
+ return [`${label}:unsafe-path`];
171
+ const existingPath = resolveEvidencePath(filePath, context);
172
+ if (!existingPath)
173
+ return [`${label}:missing-file`];
174
+ if (statSync(existingPath).size <= 0)
175
+ return [`${label}:empty-file`];
176
+ return [];
177
+ }
178
+ function validateGameAssetPairEvidence(input) {
179
+ const { label, spec, evidence, expectedCategory, expectedAssets, context } = input;
180
+ if (!evidence)
181
+ return [`evidence:${label}:missing`];
182
+ const blockers = [];
183
+ if (evidence.category !== expectedCategory) {
184
+ blockers.push(`evidence:${label}:category-mismatch:${evidence.category}`);
185
+ }
186
+ for (const assetId of expectedAssets) {
187
+ if (!evidence.assets.includes(assetId))
188
+ blockers.push(`evidence:${label}:asset-missing:${assetId}`);
189
+ }
190
+ blockers.push(...validateEvidenceFile(`evidence:${label}:screenshot`, evidence.screenshotEvidence, context));
191
+ if (evidence.verdict === "pass")
192
+ blockers.push(...validateCurrentRoutePrimaryScreenshotBinding({ label, spec, evidence, context }));
193
+ if (evidence.verdict !== "pass")
194
+ blockers.push(`evidence:${label}:verdict-not-pass:${evidence.verdict}`);
195
+ if (!evidence.notes.trim())
196
+ blockers.push(`evidence:${label}:notes-missing`);
197
+ for (const blocker of evidence.blockers) {
198
+ blockers.push(`evidence:${label}:blocker:${blocker}`);
199
+ }
200
+ return blockers;
201
+ }
202
+ function validateCurrentRoutePrimaryScreenshotBinding(input) {
203
+ const { label, spec, evidence, context } = input;
204
+ const blockers = [];
205
+ if (evidence.screenshotEvidence !== spec.evidence.routePrimaryScreenshot) {
206
+ blockers.push(`evidence:${label}:screenshot-not-current-route-primary`);
207
+ }
208
+ if (!evidence.routePrimaryProbe) {
209
+ blockers.push(`evidence:${label}:route-primary-probe-missing`);
210
+ }
211
+ else if (evidence.routePrimaryProbe !== spec.evidence.routePrimaryProbe) {
212
+ blockers.push(`evidence:${label}:route-primary-probe-mismatch`);
213
+ }
214
+ if (!isSha256(evidence.screenshotSha256)) {
215
+ blockers.push(`evidence:${label}:screenshot-sha256-missing`);
216
+ }
217
+ const parsed = readJsonFile(spec.evidence.routePrimaryProbe, `evidence:${label}:route-primary-probe`, context);
218
+ if (!parsed.ok) {
219
+ blockers.push(...parsed.blockers);
220
+ return blockers;
221
+ }
222
+ if (!isRecord(parsed.value)) {
223
+ blockers.push(`evidence:${label}:route-primary-probe-invalid-json`);
224
+ return blockers;
225
+ }
226
+ const retainedSha256 = routePrimaryScreenshotSha256(parsed.value, spec.evidence.routePrimaryScreenshot);
227
+ if (!retainedSha256) {
228
+ blockers.push(`evidence:${label}:route-primary-sha256-missing`);
229
+ }
230
+ else if (evidence.screenshotSha256 !== retainedSha256) {
231
+ blockers.push(`evidence:${label}:screenshot-sha256-mismatch`);
232
+ }
233
+ if (isSha256(evidence.screenshotSha256)) {
234
+ blockers.push(...validatePngFileSha256(`evidence:${label}:screenshot`, evidence.screenshotEvidence, evidence.screenshotSha256, context));
235
+ }
236
+ return blockers;
237
+ }
238
+ function isSafeRelativePath(filePath) {
239
+ return !isAbsolute(filePath) && !filePath.split(/[\\/]+/).includes("..");
240
+ }
241
+ function readJsonFile(filePath, label, context = {}) {
242
+ const blockers = validateEvidenceFile(label, filePath, context);
243
+ if (blockers.length > 0)
244
+ return { ok: false, blockers };
245
+ const existingPath = resolveEvidencePath(filePath, context);
246
+ if (!existingPath)
247
+ return { ok: false, blockers: [`${label}:missing-file`] };
248
+ try {
249
+ return { ok: true, value: JSON.parse(readFileSync(existingPath, "utf8")) };
250
+ }
251
+ catch (error) {
252
+ if (error instanceof Error)
253
+ return { ok: false, blockers: [`${label}:invalid-json`] };
254
+ throw error;
255
+ }
256
+ }
257
+ function resolveEvidencePath(filePath, context) {
258
+ if (existsSync(filePath))
259
+ return filePath;
260
+ if (context.artifactRoot) {
261
+ const artifactPath = join(context.artifactRoot, filePath);
262
+ if (existsSync(artifactPath))
263
+ return artifactPath;
264
+ }
265
+ return undefined;
266
+ }
267
+ function validateRacingTrackTopologyEvidence(spec, value, context) {
268
+ if (!isRecord(value))
269
+ return ["evidence:racing-track-topology:invalid-json"];
270
+ const racing = spec.racing;
271
+ if (!racing)
272
+ return ["evidence:racing-track-topology:not-racing-spec"];
273
+ const route = value.route;
274
+ const points = isRecord(route) && Array.isArray(route.points) ? route.points : [];
275
+ const checkpoints = isRecord(route) && Array.isArray(route.checkpoints) ? route.checkpoints : [];
276
+ const topology = recordValue(value.topology);
277
+ const meshExtraction = recordValue(value.meshExtraction);
278
+ const roadCenterline = topology && Array.isArray(topology.roadCenterline) ? topology.roadCenterline : [];
279
+ const topologyCheckpoints = topology && Array.isArray(topology.checkpoints) ? topology.checkpoints : [];
280
+ const blockers = [];
281
+ if (value.schema !== "aura3d-racing-track-topology/1.0")
282
+ blockers.push("evidence:racing-track-topology:schema-mismatch");
283
+ if (value.routeId !== spec.routeId)
284
+ blockers.push("evidence:racing-track-topology:route-mismatch");
285
+ if (value.vehicleAsset !== racing.vehicleAsset)
286
+ blockers.push("evidence:racing-track-topology:vehicle-mismatch");
287
+ if (value.trackAsset !== racing.trackAsset)
288
+ blockers.push("evidence:racing-track-topology:track-mismatch");
289
+ if (value.topologySource !== racing.raceDesign.visibleTrackTopology)
290
+ blockers.push("evidence:racing-track-topology:source-mismatch");
291
+ if (!isReleaseSafeRacingTopology(stringValue(value.topologySource))) {
292
+ blockers.push(`evidence:racing-track-topology:topology-not-release-safe:${stringValue(value.topologySource)}`);
293
+ }
294
+ blockers.push(...validateRacingAssetBinding(spec, value));
295
+ if (!topology) {
296
+ blockers.push("evidence:racing-track-topology:missing-structured-topology");
297
+ }
298
+ else {
299
+ if (topology.assetId !== racing.trackAsset)
300
+ blockers.push("evidence:racing-track-topology:structured-topology-track-mismatch");
301
+ if (!isPublicGameGeometrySource(topology.source)) {
302
+ blockers.push(`evidence:racing-track-topology:structured-topology-source-not-overlay-validated:${stringValue(topology.source)}`);
303
+ }
304
+ if (stringValue(topology.source) !== "asset-mesh-extracted") {
305
+ blockers.push(...validateCurrentRouteOverlayEvidence("evidence:racing-track-topology", topology.evidence, spec));
306
+ }
307
+ if (typeof topology.assetHash !== "string" || !topology.assetHash.startsWith("sha256-")) {
308
+ blockers.push("evidence:racing-track-topology:structured-topology-asset-hash-missing");
309
+ }
310
+ if (typeof value.assetHash !== "string" || !value.assetHash.startsWith("sha256-")) {
311
+ blockers.push("evidence:racing-track-topology:asset-hash-missing");
312
+ }
313
+ else if (topology.assetHash !== value.assetHash) {
314
+ blockers.push("evidence:racing-track-topology:asset-hash-mismatch");
315
+ }
316
+ if (roadCenterline.length < 8)
317
+ blockers.push(`evidence:racing-track-topology:structured-topology-too-few-points:${roadCenterline.length}`);
318
+ if (topologyCheckpoints.length < racing.raceDesign.minCheckpoints) {
319
+ blockers.push(`evidence:racing-track-topology:structured-topology-too-few-checkpoints:${topologyCheckpoints.length}`);
320
+ }
321
+ if (numberValue(topology.estimatedLapSeconds) < racing.raceDesign.minLapSeconds) {
322
+ blockers.push(`evidence:racing-track-topology:structured-topology-lap-seconds-too-low:${numberValue(topology.estimatedLapSeconds)}`);
323
+ }
324
+ if (numberValue(topology.confidence) < 0.65)
325
+ blockers.push(`evidence:racing-track-topology:confidence-too-low:${numberValue(topology.confidence)}`);
326
+ blockers.push(...validateGeometryModelAlignment({
327
+ label: "evidence:racing-track-topology:model-alignment",
328
+ value: topology.modelAlignment,
329
+ secondAxis: "z",
330
+ spec
331
+ }));
332
+ blockers.push(...validateLiveRacingMeshExtraction(spec, topology, racing.raceDesign.minCheckpoints, context));
333
+ }
334
+ if (value.pass !== true)
335
+ blockers.push("evidence:racing-track-topology:not-passing");
336
+ if (points.length < 8)
337
+ blockers.push(`evidence:racing-track-topology:too-few-points:${points.length}`);
338
+ if (checkpoints.length < racing.raceDesign.minCheckpoints) {
339
+ blockers.push(`evidence:racing-track-topology:too-few-checkpoints:${checkpoints.length}`);
340
+ }
341
+ if (numberValue(value.authoredLapSeconds) < racing.raceDesign.minLapSeconds) {
342
+ blockers.push(`evidence:racing-track-topology:authored-lap-seconds-too-low:${numberValue(value.authoredLapSeconds)}`);
343
+ }
344
+ if (value.routeAlignedToTrackAsset !== true)
345
+ blockers.push("evidence:racing-track-topology:route-not-aligned");
346
+ if (value.carTrackScaleCompatible !== true)
347
+ blockers.push("evidence:racing-track-topology:scale-incompatible");
348
+ if (value.noDebugLocatorDisk !== true)
349
+ blockers.push("evidence:racing-track-topology:debug-locator-disk-present");
350
+ if (meshExtraction) {
351
+ if (!isPassingGeometryExtractionStatus(meshExtraction.status))
352
+ blockers.push("evidence:racing-track-topology:mesh-extraction-not-passing");
353
+ blockers.push(...stringArrayValue(meshExtraction.blockers).map((blocker) => `evidence:racing-track-topology:${blocker}`));
354
+ }
355
+ return blockers;
356
+ }
357
+ function validatePlatformerPlayableSurfaceEvidence(spec, value, context) {
358
+ if (!isRecord(value))
359
+ return ["evidence:platformer-playable-surface:invalid-json"];
360
+ const platformer = spec.platformer;
361
+ if (!platformer)
362
+ return ["evidence:platformer-playable-surface:not-platformer-spec"];
363
+ const surfaces = Array.isArray(value.surfaces) ? value.surfaces : [];
364
+ const checkpoints = Array.isArray(value.checkpoints) ? value.checkpoints : [];
365
+ const hazards = Array.isArray(value.hazards) ? value.hazards : [];
366
+ const surfaceMap = recordValue(value.surfaceMap);
367
+ const meshExtraction = recordValue(value.meshExtraction);
368
+ const mapSurfaces = surfaceMap && Array.isArray(surfaceMap.surfaces) ? surfaceMap.surfaces : [];
369
+ const playableMapSurfaceCount = countPublicPlayablePlatformerSurfaces(mapSurfaces);
370
+ const blockers = [];
371
+ if (value.schema !== "aura3d-platformer-playable-surfaces/1.0")
372
+ blockers.push("evidence:platformer-playable-surface:schema-mismatch");
373
+ if (value.routeId !== spec.routeId)
374
+ blockers.push("evidence:platformer-playable-surface:route-mismatch");
375
+ if (value.characterAsset !== platformer.characterAsset)
376
+ blockers.push("evidence:platformer-playable-surface:character-mismatch");
377
+ if (!sameStringSet(value.worldAssets, platformer.worldAssets))
378
+ blockers.push("evidence:platformer-playable-surface:world-assets-mismatch");
379
+ if (value.surfaceSource !== platformer.levelDesign.playableSurfaceSource) {
380
+ blockers.push("evidence:platformer-playable-surface:source-mismatch");
381
+ }
382
+ if (!isReleaseSafePlatformerSurfaceSource(stringValue(value.surfaceSource))) {
383
+ blockers.push(`evidence:platformer-playable-surface:not-release-safe:${stringValue(value.surfaceSource)}`);
384
+ }
385
+ blockers.push(...validatePlatformerAssetBindings(spec, value));
386
+ if (!surfaceMap) {
387
+ blockers.push("evidence:platformer-playable-surface:missing-structured-surface-map");
388
+ }
389
+ else {
390
+ if (!platformer.worldAssets.includes(stringValue(surfaceMap.assetId))) {
391
+ blockers.push("evidence:platformer-playable-surface:structured-surface-map-world-mismatch");
392
+ }
393
+ if (!isPublicGameGeometrySource(surfaceMap.source)) {
394
+ blockers.push(`evidence:platformer-playable-surface:structured-surface-map-source-not-overlay-validated:${stringValue(surfaceMap.source)}`);
395
+ }
396
+ if (stringValue(surfaceMap.source) !== "asset-mesh-extracted") {
397
+ blockers.push(...validateCurrentRouteOverlayEvidence("evidence:platformer-playable-surface", surfaceMap.evidence, spec));
398
+ }
399
+ if (typeof surfaceMap.assetHash !== "string" || !surfaceMap.assetHash.startsWith("sha256-")) {
400
+ blockers.push("evidence:platformer-playable-surface:structured-surface-map-asset-hash-missing");
401
+ }
402
+ if (typeof value.assetHash !== "string" || !value.assetHash.startsWith("sha256-")) {
403
+ blockers.push("evidence:platformer-playable-surface:asset-hash-missing");
404
+ }
405
+ else if (surfaceMap.assetHash !== value.assetHash) {
406
+ blockers.push("evidence:platformer-playable-surface:asset-hash-mismatch");
407
+ }
408
+ if (playableMapSurfaceCount < 5) {
409
+ blockers.push(`evidence:platformer-playable-surface:structured-surface-map-too-few-playable-surfaces:${playableMapSurfaceCount}`);
410
+ }
411
+ if (numberValue(surfaceMap.estimatedCompletionSeconds) < platformer.levelDesign.minPlayableSeconds) {
412
+ blockers.push(`evidence:platformer-playable-surface:structured-surface-map-seconds-too-low:${numberValue(surfaceMap.estimatedCompletionSeconds)}`);
413
+ }
414
+ if (numberValue(surfaceMap.characterScaleRatio) <= 0 || numberValue(surfaceMap.characterScaleRatio) > 1.25) {
415
+ blockers.push(`evidence:platformer-playable-surface:character-scale-ratio-invalid:${numberValue(surfaceMap.characterScaleRatio)}`);
416
+ }
417
+ if (numberValue(surfaceMap.confidence) < 0.65) {
418
+ blockers.push(`evidence:platformer-playable-surface:confidence-too-low:${numberValue(surfaceMap.confidence)}`);
419
+ }
420
+ blockers.push(...validateGeometryModelAlignment({
421
+ label: "evidence:platformer-playable-surface:model-alignment",
422
+ value: surfaceMap.modelAlignment,
423
+ secondAxis: "y",
424
+ spec
425
+ }));
426
+ blockers.push(...validateLivePlatformerMeshExtraction(spec, surfaceMap, context));
427
+ }
428
+ if (value.pass !== true)
429
+ blockers.push("evidence:platformer-playable-surface:not-passing");
430
+ if (surfaces.length < 4)
431
+ blockers.push(`evidence:platformer-playable-surface:too-few-surfaces:${surfaces.length}`);
432
+ if (checkpoints.length < platformer.levelDesign.minCheckpoints) {
433
+ blockers.push(`evidence:platformer-playable-surface:too-few-checkpoints:${checkpoints.length}`);
434
+ }
435
+ if (platformer.levelDesign.requiresHazardRespawn && hazards.length === 0) {
436
+ blockers.push("evidence:platformer-playable-surface:missing-hazards");
437
+ }
438
+ if (!isRecord(value.finish) || numberValue(value.finish.x) <= 0)
439
+ blockers.push("evidence:platformer-playable-surface:missing-finish");
440
+ if (numberValue(value.authoredPlayableSeconds) < platformer.levelDesign.minPlayableSeconds) {
441
+ blockers.push(`evidence:platformer-playable-surface:authored-playable-seconds-too-low:${numberValue(value.authoredPlayableSeconds)}`);
442
+ }
443
+ if (value.styleCompatible !== true)
444
+ blockers.push("evidence:platformer-playable-surface:style-incompatible");
445
+ if (value.scaleCompatible !== true)
446
+ blockers.push("evidence:platformer-playable-surface:scale-incompatible");
447
+ if (value.primitivePrimaryWorldRejected !== true)
448
+ blockers.push("evidence:platformer-playable-surface:primitive-primary-world-not-rejected");
449
+ if (meshExtraction) {
450
+ if (!isPassingGeometryExtractionStatus(meshExtraction.status))
451
+ blockers.push("evidence:platformer-playable-surface:mesh-extraction-not-passing");
452
+ blockers.push(...stringArrayValue(meshExtraction.blockers).map((blocker) => `evidence:platformer-playable-surface:${blocker}`));
453
+ }
454
+ return blockers;
455
+ }
456
+ function validateLiveRacingMeshExtraction(spec, topology, minCheckpoints, context) {
457
+ if (stringValue(topology.source) !== "asset-mesh-extracted")
458
+ return [];
459
+ const racing = spec.racing;
460
+ if (!racing)
461
+ return [];
462
+ const extracted = extractRacingTrackTopologyFromAsset(racing.trackAsset, { projectDir: context.projectDir });
463
+ if (!extracted.ok) {
464
+ return [
465
+ "evidence:racing-track-topology:mesh-extraction-not-passing",
466
+ ...extracted.blockers.map((blocker) => `evidence:racing-track-topology:${blocker}`)
467
+ ];
468
+ }
469
+ const blockers = [];
470
+ if (extracted.value.assetHash !== stringValue(topology.assetHash)) {
471
+ blockers.push("evidence:racing-track-topology:live-asset-hash-mismatch");
472
+ }
473
+ if (extracted.value.checkpoints.length < minCheckpoints) {
474
+ blockers.push(`evidence:racing-track-topology:live-extraction-too-few-checkpoints:${extracted.value.checkpoints.length}`);
475
+ }
476
+ if (extracted.value.roadCenterline.length < 8) {
477
+ blockers.push(`evidence:racing-track-topology:live-extraction-too-few-points:${extracted.value.roadCenterline.length}`);
478
+ }
479
+ return blockers;
480
+ }
481
+ function validateLivePlatformerMeshExtraction(spec, surfaceMap, context) {
482
+ if (stringValue(surfaceMap.source) !== "asset-mesh-extracted")
483
+ return [];
484
+ const platformer = spec.platformer;
485
+ if (!platformer)
486
+ return [];
487
+ const worldAsset = stringValue(surfaceMap.assetId);
488
+ if (!platformer.worldAssets.includes(worldAsset))
489
+ return ["evidence:platformer-playable-surface:live-extraction-world-mismatch"];
490
+ const extracted = extractPlatformerPlayableSurfaceMapFromAsset(worldAsset, { projectDir: context.projectDir });
491
+ if (!extracted.ok) {
492
+ return [
493
+ "evidence:platformer-playable-surface:mesh-extraction-not-passing",
494
+ ...extracted.blockers.map((blocker) => `evidence:platformer-playable-surface:${blocker}`)
495
+ ];
496
+ }
497
+ const blockers = [];
498
+ if (extracted.value.assetHash !== stringValue(surfaceMap.assetHash)) {
499
+ blockers.push("evidence:platformer-playable-surface:live-asset-hash-mismatch");
500
+ }
501
+ const playableSurfaceCount = countPublicPlayablePlatformerSurfaces(extracted.value.surfaces);
502
+ if (playableSurfaceCount < 5) {
503
+ blockers.push(`evidence:platformer-playable-surface:live-extraction-too-few-playable-surfaces:${playableSurfaceCount}`);
504
+ }
505
+ if (extracted.value.estimatedCompletionSeconds < platformer.levelDesign.minPlayableSeconds) {
506
+ blockers.push(`evidence:platformer-playable-surface:live-extraction-seconds-too-low:${extracted.value.estimatedCompletionSeconds}`);
507
+ }
508
+ return blockers;
509
+ }
510
+ function validateRacingAssetBinding(spec, value) {
511
+ const racing = spec.racing;
512
+ if (!racing)
513
+ return [];
514
+ const assetBinding = recordValue(value.assetBinding);
515
+ if (value.topologySource === "mesh-road-topology")
516
+ return [];
517
+ const blockers = [];
518
+ if (!assetBinding)
519
+ return ["evidence:racing-track-topology:missing-asset-binding"];
520
+ if (assetBinding.kind !== "aura-game-asset-bound-racing-route") {
521
+ blockers.push("evidence:racing-track-topology:asset-binding-kind-mismatch");
522
+ }
523
+ if (assetBinding.vehicleAsset !== racing.vehicleAsset) {
524
+ blockers.push("evidence:racing-track-topology:asset-binding-vehicle-mismatch");
525
+ }
526
+ if (assetBinding.trackAsset !== racing.trackAsset) {
527
+ blockers.push("evidence:racing-track-topology:asset-binding-track-mismatch");
528
+ }
529
+ if (typeof assetBinding.trackAssetHash !== "string" || !assetBinding.trackAssetHash.startsWith("sha256-")) {
530
+ blockers.push("evidence:racing-track-topology:asset-binding-track-hash-missing");
531
+ }
532
+ else if (typeof value.assetHash === "string" && assetBinding.trackAssetHash !== value.assetHash) {
533
+ blockers.push("evidence:racing-track-topology:asset-binding-track-hash-mismatch");
534
+ }
535
+ if (!isPublicGameGeometrySource(assetBinding.topologySource)) {
536
+ blockers.push(`evidence:racing-track-topology:asset-binding-source-not-overlay-validated:${stringValue(assetBinding.topologySource)}`);
537
+ }
538
+ if (assetBinding.layoutContractVersion !== "1.0") {
539
+ blockers.push("evidence:racing-track-topology:asset-binding-version-missing");
540
+ }
541
+ return blockers;
542
+ }
543
+ function validatePlatformerAssetBindings(spec, value) {
544
+ const platformer = spec.platformer;
545
+ if (!platformer)
546
+ return [];
547
+ if (value.surfaceSource === "asset-derived-playable-surfaces")
548
+ return [];
549
+ const assetBindings = Array.isArray(value.assetBindings) ? value.assetBindings : [];
550
+ const blockers = [];
551
+ if (assetBindings.length === 0)
552
+ return ["evidence:platformer-playable-surface:missing-asset-bindings"];
553
+ const boundWorlds = new Set();
554
+ for (const bindingValue of assetBindings) {
555
+ const binding = recordValue(bindingValue);
556
+ if (!binding) {
557
+ blockers.push("evidence:platformer-playable-surface:asset-binding-invalid");
558
+ continue;
559
+ }
560
+ if (binding.kind !== "aura-game-asset-bound-platformer-level") {
561
+ blockers.push("evidence:platformer-playable-surface:asset-binding-kind-mismatch");
562
+ }
563
+ if (binding.characterAsset !== platformer.characterAsset) {
564
+ blockers.push("evidence:platformer-playable-surface:asset-binding-character-mismatch");
565
+ }
566
+ if (binding.layoutContractVersion !== "1.0") {
567
+ blockers.push("evidence:platformer-playable-surface:asset-binding-version-missing");
568
+ }
569
+ const worldAsset = typeof binding.worldAsset === "string" ? binding.worldAsset : undefined;
570
+ if (worldAsset)
571
+ boundWorlds.add(worldAsset);
572
+ if (binding.worldAssetHash === undefined) {
573
+ blockers.push(`evidence:platformer-playable-surface:asset-binding-world-hash-missing:${worldAsset ?? "unknown"}`);
574
+ }
575
+ else if (typeof binding.worldAssetHash !== "string" || !binding.worldAssetHash.startsWith("sha256-")) {
576
+ blockers.push(`evidence:platformer-playable-surface:asset-binding-world-hash-invalid:${worldAsset ?? "unknown"}`);
577
+ }
578
+ else if (typeof value.assetHash === "string" && binding.worldAssetHash !== value.assetHash) {
579
+ blockers.push(`evidence:platformer-playable-surface:asset-binding-world-hash-mismatch:${worldAsset ?? "unknown"}`);
580
+ }
581
+ if (!isPublicGameGeometrySource(binding.surfaceSource)) {
582
+ blockers.push(`evidence:platformer-playable-surface:asset-binding-source-not-overlay-validated:${worldAsset ?? "unknown"}:${stringValue(binding.surfaceSource)}`);
583
+ }
584
+ const surfaceIds = Array.isArray(binding.surfaceIds) ? binding.surfaceIds.filter((surfaceId) => typeof surfaceId === "string") : [];
585
+ if (surfaceIds.length === 0) {
586
+ blockers.push(`evidence:platformer-playable-surface:asset-binding-empty-surfaces:${worldAsset ?? "unknown"}`);
587
+ }
588
+ }
589
+ for (const worldAsset of platformer.worldAssets) {
590
+ if (!boundWorlds.has(worldAsset))
591
+ blockers.push(`evidence:platformer-playable-surface:asset-binding-world-missing:${worldAsset}`);
592
+ }
593
+ return blockers;
594
+ }
595
+ function isReleaseSafeRacingTopology(source) {
596
+ return source === "asset-bound-road-topology" || source === "mesh-road-topology";
597
+ }
598
+ function isReleaseSafePlatformerSurfaceSource(source) {
599
+ return source === "asset-bound-playable-surfaces" || source === "asset-derived-playable-surfaces";
600
+ }
601
+ function isPublicGameGeometrySource(source) {
602
+ const value = stringValue(source);
603
+ return value === "asset-mesh-extracted" || value === "manifest-authored-overlay-validated" || value === "compiler-authored-overlay-validated";
604
+ }
605
+ function isPassingGeometryExtractionStatus(status) {
606
+ return status === "pass" || status === "overlay-validated";
607
+ }
608
+ function validateCurrentRouteOverlayEvidence(label, value, spec) {
609
+ const evidence = recordValue(value);
610
+ if (typeof evidence?.routeOverlay !== "string" || evidence.routeOverlay.length === 0)
611
+ return [`${label}:missing-route-overlay-evidence`];
612
+ return evidence.routeOverlay === spec.evidence.routePrimaryScreenshot ? [] : [`${label}:route-overlay-not-current-route-primary`];
613
+ }
614
+ function validateGeometryModelAlignment(input) {
615
+ const { label, value, secondAxis, spec } = input;
616
+ const alignment = recordValue(value);
617
+ if (!alignment)
618
+ return [`${label}:missing`];
619
+ const blockers = [];
620
+ const modelBounds = recordValue(alignment.modelBounds);
621
+ const modelMin = Array.isArray(modelBounds?.min) ? modelBounds.min : undefined;
622
+ const modelMax = Array.isArray(modelBounds?.max) ? modelBounds.max : undefined;
623
+ const modelPoint = Array.isArray(alignment.modelPoint) ? alignment.modelPoint : undefined;
624
+ const gamePoint = recordValue(alignment.gamePoint);
625
+ const alignmentSource = stringValue(alignment.source);
626
+ const anchorPairs = Array.isArray(alignment.anchorPairs) ? alignment.anchorPairs : [];
627
+ if (!isPublicGameGeometrySource(alignmentSource)) {
628
+ blockers.push(`${label}:source-not-overlay-validated:${alignmentSource}`);
629
+ }
630
+ if (alignmentSource !== "asset-mesh-extracted") {
631
+ blockers.push(...validateCurrentRouteOverlayEvidence(label, alignment.evidence, spec));
632
+ if (!Array.isArray(alignment.anchorPairs)) {
633
+ blockers.push(`${label}:anchor-pairs-missing`);
634
+ }
635
+ else if (anchorPairs.length < 2) {
636
+ blockers.push(`${label}:too-few-anchor-pairs:${anchorPairs.length}`);
637
+ }
638
+ }
639
+ if (!modelBounds) {
640
+ blockers.push(`${label}:missing-model-bounds`);
641
+ }
642
+ else {
643
+ blockers.push(...validateVec3(`${label}:model-bounds-min`, modelMin));
644
+ blockers.push(...validateVec3(`${label}:model-bounds-max`, modelMax));
645
+ if (modelMin && modelMax) {
646
+ const dimensions = modelMin.map((min, index) => numberValue(modelMax[index]) - numberValue(min));
647
+ if (dimensions.some((dimension) => dimension <= 0))
648
+ blockers.push(`${label}:invalid-model-bounds`);
649
+ }
650
+ }
651
+ blockers.push(...validateVec3(`${label}:model-point`, modelPoint));
652
+ if (modelMin && modelMax && modelPoint && blockers.length === 0) {
653
+ for (let index = 0; index < 3; index += 1) {
654
+ const point = numberValue(modelPoint[index]);
655
+ if (point < numberValue(modelMin[index]) || point > numberValue(modelMax[index])) {
656
+ blockers.push(`${label}:model-point-outside-bounds`);
657
+ break;
658
+ }
659
+ }
660
+ }
661
+ if (!gamePoint) {
662
+ blockers.push(`${label}:missing-game-point`);
663
+ }
664
+ else {
665
+ if (!hasFiniteNumber(gamePoint.x))
666
+ blockers.push(`${label}:missing-game-point-x`);
667
+ if (!hasFiniteNumber(gamePoint[secondAxis]))
668
+ blockers.push(`${label}:missing-game-point-${secondAxis}`);
669
+ }
670
+ blockers.push(...validateGeometryAnchorPairs({
671
+ label,
672
+ values: anchorPairs,
673
+ secondAxis,
674
+ modelMin,
675
+ modelMax
676
+ }));
677
+ const evidence = recordValue(alignment.evidence);
678
+ if (!evidence || stringValue(evidence.notes) === "missing")
679
+ blockers.push(`${label}:missing-notes`);
680
+ return blockers;
681
+ }
682
+ function validateGeometryAnchorPairs(input) {
683
+ const blockers = [];
684
+ for (const [index, value] of input.values.entries()) {
685
+ const anchor = recordValue(value);
686
+ if (!anchor) {
687
+ blockers.push(`${input.label}:anchor-pair-invalid:${index}`);
688
+ continue;
689
+ }
690
+ if (typeof anchor.id !== "string" || anchor.id.trim().length === 0) {
691
+ blockers.push(`${input.label}:anchor-pair-id-missing:${index}`);
692
+ }
693
+ const modelPoint = Array.isArray(anchor.modelPoint) ? anchor.modelPoint : undefined;
694
+ blockers.push(...validateVec3(`${input.label}:anchor-pair-model-point:${index}`, modelPoint));
695
+ if (input.modelMin && input.modelMax && modelPoint) {
696
+ for (let coordinate = 0; coordinate < 3; coordinate += 1) {
697
+ const point = numberValue(modelPoint[coordinate]);
698
+ if (point < numberValue(input.modelMin[coordinate]) || point > numberValue(input.modelMax[coordinate])) {
699
+ blockers.push(`${input.label}:anchor-pair-model-point-outside-bounds:${index}`);
700
+ break;
701
+ }
702
+ }
703
+ }
704
+ const gamePoint = recordValue(anchor.gamePoint);
705
+ if (!gamePoint) {
706
+ blockers.push(`${input.label}:anchor-pair-game-point-missing:${index}`);
707
+ }
708
+ else {
709
+ if (!hasFiniteNumber(gamePoint.x))
710
+ blockers.push(`${input.label}:anchor-pair-game-point-x-missing:${index}`);
711
+ if (!hasFiniteNumber(gamePoint[input.secondAxis])) {
712
+ blockers.push(`${input.label}:anchor-pair-game-point-${input.secondAxis}-missing:${index}`);
713
+ }
714
+ }
715
+ }
716
+ return blockers;
717
+ }
718
+ function validateVec3(label, value) {
719
+ if (!value || value.length !== 3)
720
+ return [`${label}:invalid`];
721
+ return value.every((item) => hasFiniteNumber(item)) ? [] : [`${label}:invalid`];
722
+ }
723
+ function hasFiniteNumber(value) {
724
+ return typeof value === "number" && Number.isFinite(value);
725
+ }
726
+ function sameStringSet(value, expected) {
727
+ if (!Array.isArray(value))
728
+ return false;
729
+ const actualSet = new Set(value.filter((item) => typeof item === "string"));
730
+ return actualSet.size === expected.length && expected.every((item) => actualSet.has(item));
731
+ }
732
+ function recordValue(value) {
733
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
734
+ }
735
+ function countPublicPlayablePlatformerSurfaces(surfaces) {
736
+ return surfaces.filter((surface) => {
737
+ const record = recordValue(surface);
738
+ return record?.kind === "ground" || record?.kind === "platform" || record?.kind === "moving";
739
+ }).length;
740
+ }
741
+ function validateDeployReport(spec, value) {
742
+ if (!isRecord(value) || !Array.isArray(value.routes))
743
+ return ["evidence:deploy-artifact:invalid-launch-evidence"];
744
+ const route = value.routes.find((candidate) => isRecord(candidate) && candidate.deployCheckCommand === spec.evidence.deployCommand);
745
+ if (!isRecord(route))
746
+ return ["evidence:deploy-artifact:command-mismatch"];
747
+ const blockers = [];
748
+ if (route.deployCheckOk !== true)
749
+ blockers.push("evidence:deploy-artifact:not-passing");
750
+ if (Array.isArray(route.deployWarnings) && route.deployWarnings.length > 0)
751
+ blockers.push("evidence:deploy-artifact:has-warnings");
752
+ if (Array.isArray(route.deployFailures) && route.deployFailures.length > 0)
753
+ blockers.push("evidence:deploy-artifact:has-failures");
754
+ for (const asset of spec.primaryAssets) {
755
+ if (!deployReportIncludesAsset(route, asset.id))
756
+ blockers.push(`evidence:deploy-artifact:missing-asset:${asset.id}`);
757
+ }
758
+ return blockers;
759
+ }
760
+ function deployReportIncludesAsset(route, assetId) {
761
+ const evidence = route.primaryAssetEvidence;
762
+ return Array.isArray(evidence) && evidence.some((asset) => isRecord(asset) && asset.id === assetId);
763
+ }
764
+ function routePrimaryAssetsInclude(probe, assetId) {
765
+ const primaryAssets = probe.primaryAssets;
766
+ return Array.isArray(primaryAssets) && primaryAssets.some((asset) => isRecord(asset) && asset.id === assetId);
767
+ }
768
+ function routePrimaryScreenshotMatches(probe, screenshotPath) {
769
+ return routePrimaryRenderedProbeForScreenshot(probe, screenshotPath) !== undefined;
770
+ }
771
+ function routePrimaryRenderedProbeForScreenshot(probe, screenshotPath) {
772
+ const primaryAssets = probe.primaryAssets;
773
+ if (!Array.isArray(primaryAssets))
774
+ return undefined;
775
+ for (const asset of primaryAssets) {
776
+ if (isRecord(asset) && isRecord(asset.renderedProbe) && asset.renderedProbe.screenshotPath === screenshotPath) {
777
+ return asset.renderedProbe;
778
+ }
779
+ }
780
+ return undefined;
781
+ }
782
+ function routePrimaryScreenshotSha256(probe, screenshotPath) {
783
+ const renderedProbe = routePrimaryRenderedProbeForScreenshot(probe, screenshotPath);
784
+ const sha256 = renderedProbe?.sha256;
785
+ return isSha256(sha256) ? sha256 : undefined;
786
+ }
787
+ function validatePngFileSha256(label, filePath, expectedSha256, context) {
788
+ const existingPath = resolveEvidencePath(filePath, context);
789
+ if (!existingPath)
790
+ return [`${label}:missing-file`];
791
+ const bytes = readFileSync(existingPath);
792
+ const blockers = [];
793
+ if (!PNG_SIGNATURE.every((byte, index) => bytes[index] === byte))
794
+ blockers.push(`${label}:not-png`);
795
+ const actualSha256 = `sha256-${createHash("sha256").update(bytes).digest("hex")}`;
796
+ if (actualSha256 !== expectedSha256)
797
+ blockers.push(`${label}:file-sha256-mismatch`);
798
+ return blockers;
799
+ }
800
+ function isSha256(value) {
801
+ return typeof value === "string" && /^sha256-[a-f0-9]{64}$/.test(value);
802
+ }
803
+ function isRecord(value) {
804
+ return typeof value === "object" && value !== null && !Array.isArray(value);
805
+ }
806
+ function numberValue(value) {
807
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
808
+ }
809
+ function stringValue(value) {
810
+ return typeof value === "string" ? value : "missing";
811
+ }
812
+ function stringArrayValue(value) {
813
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
814
+ }
815
+ //# sourceMappingURL=showcase-spec-evidence.js.map