@aura3d/cli 4.0.8 → 4.1.1

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 (55) hide show
  1. package/data/aura.exports.json +10 -10
  2. package/data/root-export-dispositions.json +504 -0
  3. package/dist/cli-help.d.ts.map +1 -1
  4. package/dist/cli-help.js +6 -0
  5. package/dist/cli-help.js.map +1 -1
  6. package/dist/cli.js +33 -0
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/assets/gltfToolkit.d.ts +25 -0
  9. package/dist/commands/assets/gltfToolkit.d.ts.map +1 -0
  10. package/dist/commands/assets/gltfToolkit.js +59 -0
  11. package/dist/commands/assets/gltfToolkit.js.map +1 -0
  12. package/dist/commands/assets/importMeshy.d.ts +50 -0
  13. package/dist/commands/assets/importMeshy.d.ts.map +1 -0
  14. package/dist/commands/assets/importMeshy.js +112 -0
  15. package/dist/commands/assets/importMeshy.js.map +1 -0
  16. package/dist/commands/assets/index.d.ts +6 -0
  17. package/dist/commands/assets/index.d.ts.map +1 -0
  18. package/dist/commands/assets/index.js +54 -0
  19. package/dist/commands/assets/index.js.map +1 -0
  20. package/dist/commands/assets/mergeClips.d.ts +44 -0
  21. package/dist/commands/assets/mergeClips.d.ts.map +1 -0
  22. package/dist/commands/assets/mergeClips.js +103 -0
  23. package/dist/commands/assets/mergeClips.js.map +1 -0
  24. package/dist/commands/assets/optimize.d.ts +54 -0
  25. package/dist/commands/assets/optimize.d.ts.map +1 -0
  26. package/dist/commands/assets/optimize.js +76 -0
  27. package/dist/commands/assets/optimize.js.map +1 -0
  28. package/dist/commands/assets/optimizeVerb.d.ts +9 -0
  29. package/dist/commands/assets/optimizeVerb.d.ts.map +1 -0
  30. package/dist/commands/assets/optimizeVerb.js +40 -0
  31. package/dist/commands/assets/optimizeVerb.js.map +1 -0
  32. package/dist/commands/meshyLedger.d.ts +103 -0
  33. package/dist/commands/meshyLedger.d.ts.map +1 -0
  34. package/dist/commands/meshyLedger.js +308 -0
  35. package/dist/commands/meshyLedger.js.map +1 -0
  36. package/dist/commands/prd05/index.d.ts.map +1 -1
  37. package/dist/commands/prd05/index.js +8 -2
  38. package/dist/commands/prd05/index.js.map +1 -1
  39. package/dist/commands/registry.d.ts +1 -0
  40. package/dist/commands/registry.d.ts.map +1 -1
  41. package/dist/commands/registry.js +1 -0
  42. package/dist/commands/registry.js.map +1 -1
  43. package/package.json +5 -5
  44. package/skills/aura3d-assets/SKILL.md +28 -26
  45. package/skills/aura3d-browser-game/SKILL.md +16 -13
  46. package/skills/aura3d-browser-game/references/kits-and-hooks.md +107 -0
  47. package/skills/aura3d-core/SKILL.md +9 -8
  48. package/skills/aura3d-core/references/pitfalls.md +62 -0
  49. package/skills/aura3d-evidence-review/SKILL.md +22 -2
  50. package/skills/aura3d-materials-environments/SKILL.md +8 -0
  51. package/skills/aura3d-materials-environments/references/custom-shader-chunks.md +53 -0
  52. package/skills/aura3d-performance/SKILL.md +17 -1
  53. package/skills/llms.txt +30 -20
  54. package/skills/meshy-cli/SKILL.md +7 -5
  55. package/skills/meshy-cli/references/asset-tooling.md +72 -0
@@ -0,0 +1,107 @@
1
+ # Kits and hooks for browser games (4.1)
2
+
3
+ Eleven ports and a tennis rebuild each copied the same shell code. The pieces below
4
+ are now library surface: start from them instead of hand-rolling a HUD, an input
5
+ store, a fixed-step loop, QA hooks or a synth. Every name here is verified against
6
+ `docs/api/*.md` and the source; if an import fails, the installed version predates
7
+ 4.1 and you stop and report instead of re-creating the kit.
8
+
9
+ ## Pick the piece
10
+
11
+ | Need | Use | Import |
12
+ | --- | --- | --- |
13
+ | Lifecycle, pause, results, DOM HUD, touch controls, juice, game sound | the game kit: `createGame`, `mountHud`, `createJuice`, `sfxUrl` | `@aura3d/engine/game` |
14
+ | Deterministic fixed-step simulation and automated checks | `createFixedStepLoop`, `installQaHooks`, `readQaParams`, `createCountedRng` | `@aura3d/engine` |
15
+ | Procedural sound cues, a mute key, ducking, step sequencer, engine drone | `createCueBank`, `createStepSequencer`, `createToneLoop`, `noteFrom`, `SCALES` | `@aura3d/engine/audio` |
16
+ | Recorded takes (several hits in one sprite sheet) | `createSampleSlices` | `@aura3d/engine/audio` |
17
+ | Rebindable keys with validation and persistence | `createBindingStore` (private `@aura3d/game` today; publishing through the game kit is the follow-up) | in-repo only |
18
+ | Racket and ball sports | `game.racketSports()`: `ballFlight`, `scoring`, `athleteRig` | `@aura3d/engine` |
19
+ | A black-frame safety net and a GPU diagnostics panel | `installSafetyNet` | `@aura3d/debug` |
20
+
21
+ `createGame` from `@aura3d/engine` is a headless stub that logs `CREATE_GAME_STUB`.
22
+ The real kit is the `@aura3d/engine/game` entry, and importing it turns the `game`
23
+ quality-rebuild lane on by default. Do not add a `qualityRebuild` flag list by hand.
24
+
25
+ ## Deterministic replay and QA hooks
26
+
27
+ A browser spec, a capture tool and the live smoke all drive the page through
28
+ `window.__AURA_<NAME>__`. Install it once with `installQaHooks(name, options)` and
29
+ keep the `state` hook plain data: never put engine objects in it.
30
+
31
+ ```ts
32
+ import { createFixedStepLoop, installQaHooks, readQaParams } from "@aura3d/engine";
33
+
34
+ const config = readQaParams({ seed: { type: "number", default: 1 }, manual: { type: "flag" } });
35
+ const loop = createFixedStepLoop({
36
+ manual: config.manual,
37
+ isActive: () => ready,
38
+ step: () => sim.advance(readInput()),
39
+ render: () => draw()
40
+ });
41
+ const qa = installQaHooks("my-game", {
42
+ ready: () => ready,
43
+ seed: config.seed,
44
+ state: () => sim.snapshot(),
45
+ stepFrames: (n, input) => { for (let i = 0; i < n; i++) sim.advance(input); draw(); },
46
+ reset: (seed) => { sim = new Sim(seed ?? config.seed); draw(); },
47
+ app: () => app
48
+ });
49
+ loop.start();
50
+ ```
51
+
52
+ Rules that came out of the ports:
53
+
54
+ - Keep the simulation a pure fixed-timestep module with a seedable RNG
55
+ (`createCountedRng` exposes a draw counter so a replay can assert it). It never
56
+ touches the DOM, `Math.random`, `Date` or `performance.now`.
57
+ - Record a scripted input run from the original or the reference build in two
58
+ independent sessions, derive the tolerance from their spread, and replay it as a
59
+ unit test. The Crayon ports match to 1e-6 or better; Aura Slam matches the
60
+ original engine bit for bit.
61
+ - Anything that advances inside the render callback (autoplay, assist swings)
62
+ makes extra renders change the game: render once per step in manual mode.
63
+ - Read `?manual=1`, `?warmup=`, `?seed=` through `readQaParams`, not by hand.
64
+
65
+ ## Production safety net
66
+
67
+ For any route you ship, install the safety net and keep `?diag=1` working. It is
68
+ opt-in: with `safety` unset the handle is inert and only parses the switches.
69
+
70
+ ```ts
71
+ import { installSafetyNet } from "@aura3d/debug";
72
+
73
+ const net = installSafetyNet({ safety: true, canvas, app });
74
+ const { post, shadow, env, quality } = net.switches; // read before building the scene
75
+ // after each drawn frame: net.sample(framesDrawn);
76
+ ```
77
+
78
+ `?post=0 ?shadow=0 ?env=0 ?sky=0 ?aa=0 ?quality=low ?tm=aces ?heal=0 ?diag=1` are
79
+ the switches; your game decides what each one changes. A canvas that stays black
80
+ for about 23 samples reloads one rung down (`no-aa`, `no-post`, `aces-tone`,
81
+ `no-env`, `no-shadow`, `low-quality`), logs `SAFETY_BLACK_FRAME`, and the next page
82
+ logs `SAFETY_COMPAT_MODE`. Never replace this with a blanket `qualityRebuild` pin
83
+ that disables post-processing for every player.
84
+
85
+ ## Sound
86
+
87
+ `createCueBank({ cues, muteKey })` sits on the page's single `AudioContextManager`,
88
+ so the one-owner rule holds: never write `new AudioContext(`. Call `bank.install()`
89
+ for first-gesture unlock and the mute key, then `bank.play(id, { level })`. Bind
90
+ `play` to simulation events, not to key presses, so replays sound the same.
91
+
92
+ ## Racket sports
93
+
94
+ `game.racketSports().ballFlight(config)` is exact event-time flight (bounce, net,
95
+ tape, boards) in court units with a different axis convention from the simple
96
+ `stepBallPhysics` (z up, net at y = 0). `scoring` is a pure game, match and
97
+ tiebreak state machine that defaults to tennis. `athleteRig` has `locomotion`,
98
+ `applyLimbs` and `solveKnee`. Line calls, AI and costumes stay game code.
99
+
100
+ ## Converting an existing game
101
+
102
+ Follow the order used for Aura Slam: capture a deterministic reference, write the
103
+ typed contract, transcribe the pure simulation behind differential tests, port
104
+ render, input, audio and UI behind that contract, then gate on replay, matching
105
+ frames, a real-input play-through and a production-build check before swapping the
106
+ route. See `docs/aura-slam/ARCHITECTURE.md` and
107
+ `docs/workflows/crayon-game-reverse-engineering.md`.
@@ -83,14 +83,14 @@ without a look ships flat shading, void background and floating props.
83
83
  fading `#boot-gate` so lighting, shadow, and GLB silhouettes are fully
84
84
  mounted; default `autopilot` to `false` (manual control on Frame 1) unless
85
85
  `?autoplay=1` / `?demo=1` is in the URL or the user clicks the HUD `Auto`
86
- button (with `button.blur()` for clean framing). Pass
87
- `qualityRebuild: { flags: ["game", "-compiler", "-post", "-vfxFog"] }` when
88
- fast arcade contrast and frame pacing are required, decimate Meshy GLBs
89
- (`<= 20,000` vertices, `<= 2 MB`, 16-bit indices, `<= 1024x1024` textures for
90
- sharp reflection and palette), keep canonical `sha256-...` `hash` provenance
91
- in `defineAuraAssets(...)` for clean look and silhouette, and mutate runtime
92
- node handles (`app.nodes.require(...)`) instead of calling `app.setScene()`
93
- on same-stage restarts so lighting, shadow, and postprocess grade never drop.
86
+ button (with `button.blur()` for clean framing), keep canonical `sha256-...`
87
+ `hash` provenance in `defineAuraAssets(...)` or `defineModelAssets(...)`, run
88
+ Meshy GLBs through `assets optimize --role` first, never pin post-processing or
89
+ the compiler off with a `qualityRebuild` list (use the safety net), and mutate
90
+ runtime node handles (`app.nodes.require(...)`) instead of calling
91
+ `app.setScene()` on same-stage restarts so lighting and grade never drop.
92
+ Build the HUD, replay hooks and sound from the kits:
93
+ [kits and hooks](../aura3d-browser-game/references/kits-and-hooks.md).
94
94
 
95
95
  ## Routing table
96
96
 
@@ -136,6 +136,7 @@ spec lives in `aura3d-art-direction`.
136
136
  ## References
137
137
 
138
138
  - [Shared boundaries](references/boundaries.md)
139
+ - [Known pitfalls](references/pitfalls.md)
139
140
  - [Agent guide llms.txt](https://github.com/auraoneai/aura3d/blob/main/llms.txt)
140
141
  - [Claims and boundaries](https://github.com/auraoneai/aura3d/blob/main/docs/agents/claims-and-boundaries.md)
141
142
  - [No hackjob rules](https://github.com/auraoneai/aura3d/blob/main/docs/agents/no-hackjob-rules.md)
@@ -0,0 +1,62 @@
1
+ # Known pitfalls (from porting eleven games and rebuilding four)
2
+
3
+ Every item below cost a debugging session and is now either fixed in 4.1 or has a
4
+ rule. Read it before you ship a route, and when a page looks wrong on a real device
5
+ but passes in CI.
6
+
7
+ ## Rendering that passes in CI and fails on a player's GPU
8
+
9
+ - **NaN from `pow` (Apple GPUs).** A custom shader's `pow` call with a base that
10
+ rounds slightly negative returns NaN on Apple silicon; bloom spreads it and the
11
+ page presents black while the engine reports hundreds of draw calls. Use the
12
+ `a3d_math` chunk helpers; 4.1 injects a total `pow` into `material.custom` and
13
+ drops non-finite samples in the output stage and bloom downsample.
14
+ - **Software GL is not real-GPU proof.** SwiftShader passed pages that were black or
15
+ missing objects on an M-series Mac. Say "software GL" in reports; install the
16
+ safety net (`installSafetyNet`, `?diag=1`, black-frame self-heal) on shipped
17
+ routes and ask for a `?diag=1` screenshot from a real device.
18
+ - **Custom shader colour.** Custom shaders write linear light that the output stage
19
+ sRGB-encodes and ACES tones down: use `#include <colorspace_decode>` and
20
+ `toneMapping: "none"` for authored colours. See `aura3d-materials-environments`.
21
+ - **Directional shadow depth range.** With a narrow-fov camera far from the scene
22
+ (fov 15 at 40 m) one small caster used to darken a third of the frame; fixed in
23
+ 4.1 by clamping receiver depth to [0, 1] in every directional sampler.
24
+ - **Instanced textures.** `instances.mesh` and `batched.mesh` drew textured models
25
+ white before 4.1. A multi-geometry `batched.mesh` also needs the `tiers.batching`
26
+ flag for packed multi-draw; without it the engine now falls back to per-geometry
27
+ instancing.
28
+ - **Empty instance arrays.** A zero-length `instances.*`, zero-position points or
29
+ lines, or a zero draw range used to throw on every render and blank the canvas.
30
+ They are now no-ops with `INSTANCES_EMPTY` / `GEOMETRY_EMPTY` diagnostics.
31
+
32
+ ## Assets and the production bridge
33
+
34
+ - The production bridge refuses `model()` assets whose URL is http(s) or that lack a
35
+ valid `sha256` and provenance, and then draws nothing while the game state keeps
36
+ advancing. Declare models with `defineModelAssets`; check every asset URL returns
37
+ 200 on the built site.
38
+ - Never run `assets import-meshy`, `validate` or `typegen` from the repo root while
39
+ other work is open: they rewrite `aura.assets.json`, `src/aura-assets.ts` (a
40
+ 100k-line diff) and `public/aura-assets/*`. Use `import-meshy --into <dir>
41
+ --prefix <game> --name <role>`.
42
+ - Prefix every raw download with the game name: unprefixed `rocks.raw.glb` files
43
+ collide across games.
44
+ - A rate-limited Meshy poll is not a failed task: record it `unconfirmed`, resume it
45
+ with `get`, and never create it again.
46
+ - A large imported model set blows past upload and budget limits: run
47
+ `assets optimize --role` and keep a game's assets under about 25 MB.
48
+
49
+ ## Bundles, builds and tooling
50
+
51
+ - Bare side-effect imports of self-registering modules are tree-shaken from
52
+ production bundles unless the root `sideEffects` allowlist covers them (4.1 covers
53
+ `./examples/**`, `./apps/**`, `./templates/**`). Test the production build, not the
54
+ dev server.
55
+ - Never `git add -A` in a worktree whose `node_modules` is a symlink, and never run
56
+ `pnpm` there: it tries to remove the linked modules directory. Call the binaries
57
+ directly.
58
+ - A deploy upload over 15,000 files fails: deploy from a clean export of a committed
59
+ commit, not the working tree.
60
+ - A game that pins post-processing or the compiler off for everyone hides a bug and
61
+ makes the look worse. Fix the bug or let the safety net step down on the one
62
+ machine that breaks.
@@ -42,6 +42,18 @@ benchmark exception are in [boundaries](../aura3d-core/references/boundaries.md)
42
42
  local agent process. It writes route-health output and
43
43
  `tests/reports/screenshot.png` plus pixel metrics. Capture desktop and
44
44
  mobile viewports and retain the PNGs with hashes.
45
+ Games and simulations: drive the page through `installQaHooks`
46
+ (`window.__AURA_<NAME>__.stepFrames/state/replay`) and compare the `state` hook with a
47
+ recorded reference whose tolerance comes from two independent sessions.
48
+ Test the **production build**, not the dev server: bundling can drop modules
49
+ and the production bridge refuses unhashed model URLs, so assert a non-blank
50
+ canvas, `drawCalls > 0`, zero console errors and 200 for every asset URL. A
51
+ state hook that advances proves nothing about pixels (a page can run its
52
+ simulation under a black canvas).
53
+ Software GL (SwiftShader) proves correctness and draw-call counts only. Ship
54
+ the safety net (`installSafetyNet`, `?diag=1`, black-frame self-heal) on any
55
+ route you deploy, ask for a real-device `?diag=1` screenshot before claiming it
56
+ works on GPUs, and label software-only runs as such.
45
57
  4. For kit-based or prompt-plan scenes, collect structured evidence:
46
58
  `collectAuraSceneEvidence(scene)` for bodies, cameras, clips, and asset
47
59
  provenance; `app.diagnostics().look` (resolved look, expansion, lint
@@ -59,7 +71,11 @@ benchmark exception are in [boundaries](../aura3d-core/references/boundaries.md)
59
71
  7. Grade cinematic scenes on the ladder: L0 text only, L1 primitive sketch,
60
72
  L2 asset-backed draft, L3 realtime cinematic scene, L4 production handoff.
61
73
  Only L3 or better is public product proof.
62
- 8. Bake-offs: when comparing a catalog candidate against a Meshy candidate,
74
+ 8. Parity and look claims against an original: report a seeded, randomised
75
+ blind A/B judge as identification accuracy against chance (50%) with trial
76
+ count and seed, and say it is a model's judgement, not a human's. "Same game
77
+ design" and "indistinguishable" are different claims; never say pixel parity
78
+ from histograms and a judge. Bake-offs: when comparing a catalog candidate against a Meshy candidate,
63
79
  use one brief, one rubric, one camera and lighting setup, and the same
64
80
  gates for both. Report both verdict sets and pick on evidence, not source.
65
81
  9. Thumbnails and posters: run `npx @aura3d/cli@latest assets thumbnail` for
@@ -80,7 +96,9 @@ benchmark exception are in [boundaries](../aura3d-core/references/boundaries.md)
80
96
  diagnoses, it does not prove pixels;
81
97
  - an in-repo scorer as release proof (use a neutral human or opposite-vendor
82
98
  model reviewer);
83
- - a report generated with missing evidence inputs, even when it exits 0.
99
+ - a report generated with missing evidence inputs, even when it exits 0;
100
+ - software-GL frames offered as proof of real-GPU behaviour;
101
+ - a live smoke that only checks the simulation state advanced.
84
102
 
85
103
  ## Stop and report
86
104
 
@@ -100,6 +118,8 @@ benchmark exception are in [boundaries](../aura3d-core/references/boundaries.md)
100
118
  ## References
101
119
 
102
120
  - [Boundaries](../aura3d-core/references/boundaries.md)
121
+ - [QA and replay kit](https://github.com/auraoneai/aura3d/blob/main/docs/api/qa-kit.md)
122
+ - [Runtime safety net](https://github.com/auraoneai/aura3d/blob/main/docs/api/safety-net.md)
103
123
  - [Verification](https://github.com/auraoneai/aura3d/blob/main/docs/agents/verification.md)
104
124
  - [Rendering proof required](https://github.com/auraoneai/aura3d/blob/main/docs/agents/rendering-proof-required.md)
105
125
  - [Cinematic scene quality](https://github.com/auraoneai/aura3d/blob/main/docs/agents/cinematic-scene-quality.md)
@@ -111,6 +111,12 @@ Checks (all steps):
111
111
  13. Screenshots (normal mode only) run through `npm run test` on CI or a remote
112
112
  worker. Hand the claim label to `aura3d-evidence-review`.
113
113
 
114
+ 14. Custom shaders (`material.custom`): write linear light, use
115
+ `#include <colorspace_decode>` with `toneMapping: "none"` for authored hex
116
+ colours, take hash/noise/sky/fog from the `a3d_*` chunks, and keep every
117
+ `pow`, `normalize`, `sqrt` and divide NaN-safe —
118
+ [custom shader chunks](references/custom-shader-chunks.md).
119
+
114
120
  ## Look-dev loop
115
121
 
116
122
  `aura3d look capture` → judge against the quality-bar rubric → `aura3d look
@@ -127,10 +133,12 @@ per round.
127
133
  - The HDRI is not 2:1, has a seam, or never reaches `iblPixelBacked`: report
128
134
  it and keep the built-in environment.
129
135
  - A PBR role is missing roughness or metalness: reject the set.
136
+ - A custom-shader colour looks grey or the frame goes black on a real GPU: fix the colour contract or the NaN source; do not pin post off.
130
137
  - Any check above was not run: the route is `prototype`, not release.
131
138
 
132
139
  ## References
133
140
 
141
+ - [Custom shader chunks (4.1)](references/custom-shader-chunks.md)
134
142
  - [Environment lighting](https://github.com/auraoneai/aura3d/blob/main/docs/rendering/environment-lighting.md)
135
143
  - [HDR, IBL, tone mapping, and color](https://github.com/auraoneai/aura3d/blob/main/docs/rendering/lighting-environment-color.md)
136
144
  - [Texture compression (KTX2/Basis)](https://github.com/auraoneai/aura3d/blob/main/docs/rendering/texture-compression.md)
@@ -0,0 +1,53 @@
1
+ # Custom shaders: colour contract, chunks and NaN safety (4.1)
2
+
3
+ `material.custom({ glsl: { vertex, fragment } })` is the escape hatch for effects the
4
+ built-in materials do not cover (sky, water, energy, toon ramps). It has a colour
5
+ contract and three failure modes that cost real time on real GPUs. Chunk names and
6
+ signatures: `docs/api/glsl-chunks.md`.
7
+
8
+ ## Colour contract
9
+
10
+ - A custom shader writes **linear light**. The output stage always sRGB-encodes it,
11
+ and the default ACES operator tones it down further, so a hex colour written
12
+ straight to `outColor` comes out greyer than authored (a day sky read grey for
13
+ exactly this reason). Post effects are not the cause: bloom, grade, vignette and
14
+ fxaa leave a custom shader's colour unchanged.
15
+ - To show a hex colour as authored: `#include <colorspace_decode>`, wrap the colour
16
+ in `a3dSrgbToLinear`, and set `output: { toneMapping: "none" }` on the app.
17
+ `<colorspace_encode>` provides `a3dLinearToSrgb` for the other direction.
18
+ - Any other tone-mapping operator still tone-maps custom colours; tune the exposure
19
+ instead of compensating in the shader.
20
+
21
+ ## Chunks
22
+
23
+ `#include <a3d_math>`, `<a3d_noise>`, `<a3d_sky>`, `<a3d_fog>`, `<a3d_triplanar>`
24
+ and `<a3d_toon>` are tested GLSL ES 3.00 helpers prefixed `a3d`. Use them instead of
25
+ pasting hashes, fbm, sky gradients or fog into every shader. Precedence when names
26
+ collide: built-in chunks, then these, then `registerShaderChunk`, then the material's
27
+ `patch`. A material that includes chunks is GLSL-only: it has no WGSL twin.
28
+
29
+ ## NaN and Inf safety
30
+
31
+ Apple GPUs return NaN where a software renderer returns a number. One NaN in a
32
+ bright pixel is smeared across the whole frame by bloom, and the page presents black
33
+ even though the engine reports hundreds of draw calls.
34
+
35
+ - `pow` with a base that rounding makes slightly negative, `normalize` of a
36
+ zero vector, `sqrt` of a negative, `acos` outside [-1, 1] and a divide by zero
37
+ are the usual sources. Use `a3dSafeDiv`, `a3dSafeSqrt`, `a3dSafeAcos` and
38
+ `a3dFinite` from `<a3d_math>`.
39
+ - The engine now injects a total `pow` into `material.custom` shaders that call
40
+ `pow`, and drops non-finite samples in the output stage and the bloom downsample.
41
+ Treat that as a net, not licence to skip clamps.
42
+ - A shader that declares an attribute or uniform it never reads is reported as
43
+ `CUSTOM_ATTRIBUTE_UNUSED` or `CUSTOM_UNIFORM_UNUSED` and still draws.
44
+
45
+ ## Related fixes that change what you see
46
+
47
+ - Directional shadows with a narrow-fov camera far from the scene (a tennis court at
48
+ fov 15) darkened up to a third of the frame before the 4.1 depth clamp. A game
49
+ that switched shadows off for that reason can switch them back on.
50
+ - `instances.mesh` and `batched.mesh` apply texture maps since 4.1; before that a
51
+ textured model drawn through them was white.
52
+ - `model()` rejects http(s) URLs and models without a valid `sha256` and provenance:
53
+ the scene then draws nothing. Declare models with `defineModelAssets`.
@@ -58,7 +58,20 @@ manual quality override invalidates every measurement that follows.
58
58
  `instances.capsule`, `instances.torus`) node with a transform list. Advanced
59
59
  PBR extensions can fall back to expanded draws, so confirm native instanced
60
60
  submissions in diagnostics before claiming one draw.
61
- 4. Distant detail: use `distanceLod` with increasing `maxDistance` levels and a
61
+ Repeated `model()` nodes of one asset share decoded textures (4.1) but still
62
+ upload geometry per node: use `instances.model` for repeats. `instances.mesh` and
63
+ `batched.mesh` apply texture maps since 4.1; older builds drew them white.
64
+ An empty instance array is a no-op with an `INSTANCES_EMPTY` diagnostic, not a
65
+ crash, so plan for zero placements instead of special-casing them.
66
+ 4. Scattered props (trees, rocks, lamps, houses): plan placements with
67
+ `scatterAlongPath` or `scatterInArea` (seeded, exclusion zones, `minDistance`,
68
+ slope and height filters), split them by distance with `splitScatterByDistance`
69
+ (detailed model near, cheap instance far), and feed `scatterTransforms` into
70
+ `instances.model` / `instances.*`. Bake static sets that share a material with
71
+ `mergeStatic` / `mergeStaticByKey` (one draw per material). Turn off shadow
72
+ casting on small props and actors: the ports went from 1,249, 1,980 and 677
73
+ draw calls to 133, about 100 and 137 this way, with the look unchanged.
74
+ Distant detail: use `distanceLod` with increasing `maxDistance` levels and a
62
75
  `hysteresis` band so the camera cannot flap between levels.
63
76
  5. Large scenes: rely on CPU frustum culling plus the static-bounds BVH. Give
64
77
  custom geometry explicit bounds via `geometry.custom`. There is no GPU
@@ -86,6 +99,8 @@ manual quality override invalidates every measurement that follows.
86
99
  - A performance change alters the rendered scene: rerun screenshot review
87
100
  through `aura3d-evidence-review` before shipping.
88
101
  - GPU timing is unavailable or noisy on the device: say so; do not fill in.
102
+ - Numbers came from a software renderer (SwiftShader): they prove draw-call
103
+ counts and correctness, not frame rate. Say "software GL" and ask for a real GPU run.
89
104
 
90
105
  ## References
91
106
 
@@ -94,4 +109,5 @@ manual quality override invalidates every measurement that follows.
94
109
  - [Bundle sizes](https://github.com/auraoneai/aura3d/blob/main/BUNDLE_SIZES.md)
95
110
  - [Texture compression](https://github.com/auraoneai/aura3d/blob/main/docs/rendering/texture-compression.md)
96
111
  - [Geometry, instancing, LOD, text](https://github.com/auraoneai/aura3d/blob/main/docs/rendering/geometry-instancing-lod-text.md)
112
+ - [Scatter and merge](https://github.com/auraoneai/aura3d/blob/main/docs/api/scatter-merge.md)
97
113
  - [Deployment](https://aura3d.auraone.ai/docs/deployment.html)
package/skills/llms.txt CHANGED
@@ -8,7 +8,7 @@ Aura3D is the AI-native TypeScript 3D SDK for browser 3D apps, interactive websi
8
8
 
9
9
  - Product: AI-native TypeScript 3D SDK for browser 3D apps, interactive websites, and prompt-to-code scene kits
10
10
  - Website: https://aura3d.auraone.ai
11
- - Current release: Aura3D 4.0.8 (per the repo manifest — the same source the build's version guard uses)
11
+ - Current release: Aura3D 4.1.1 (per the repo manifest — the same source the build's version guard uses)
12
12
  - Docs: https://aura3d.auraone.ai/docs/api.html
13
13
  - Showcase game: https://aura3d.auraone.ai/showcase/aura-clash/
14
14
  - Sitemap: https://aura3d.auraone.ai/sitemap.xml
@@ -31,8 +31,8 @@ Aura3D is the AI-native TypeScript 3D SDK for browser 3D apps, interactive websi
31
31
  - [Aura3D Guide for Claude-Assisted 3D Apps](https://aura3d.auraone.ai/docs/claude.html): Use Claude with Aura3D to generate TypeScript browser 3D scenes from scene kits, primitives, typed GLB/glTF assets, and Vite templates.
32
32
  - [Aura3D Guide for Cursor AI 3D Development](https://aura3d.auraone.ai/docs/cursor.html): Use Cursor with Aura3D projects to edit TypeScript browser 3D scenes, Vite templates, typed GLB/glTF assets, scene kits, and prompt-to-3D workflows.
33
33
  - [Aura3D Evidence and Release Gates](https://aura3d.auraone.ai/docs/evidence.html): How Aura3D binds public package, asset, browser, game, visual, and deployment claims to retained evidence.
34
- - [Aura3D Claims, Limits, and Release Scope](https://aura3d.auraone.ai/docs/claims.html): Current Aura3D 4.0.8 claim boundaries for the root API, game presentations, assets, rendering, WebGPU, and diagnostics.
35
- - [Aura3D 4.0.8 vs. Raw Three.js — Open-Source TypeScript 3D Engine](https://aura3d.auraone.ai/docs/aura3d-vs-threejs.html): Stop assembling 14 fragile npm packages to render a 3D scene. Compare Aura3D 4.0.8 (@aura3d/engine@4.0.8) against raw Three.js across architecture, asset type safety, WebGPU compute, physics, and 18-line scene boot.
34
+ - [Aura3D Claims, Limits, and Release Scope](https://aura3d.auraone.ai/docs/claims.html): Current Aura3D 4.1.1 claim boundaries for the root API, game presentations, assets, rendering, WebGPU, and diagnostics.
35
+ - [Aura3D 4.1.1 vs. Raw Three.js — Open-Source TypeScript 3D Engine](https://aura3d.auraone.ai/docs/aura3d-vs-threejs.html): Stop assembling 14 fragile npm packages to render a 3D scene. Compare Aura3D 4.1.1 (@aura3d/engine@4.1.1) against raw Three.js across architecture, asset type safety, WebGPU compute, physics, and 18-line scene boot.
36
36
  - [Aura Clash: Aura3D Browser Fighting-Game Runtime Proof](https://aura3d.auraone.ai/docs/aura-clash-showcase.html): Aura Clash Arena is the Aura3D browser fighting-game development showcase: typed GLB runtime evidence, arena visuals, gameplay UI, accessibility controls, evidence routes, and release gates.
37
37
 
38
38
  ### Showcase
@@ -63,17 +63,17 @@ Aura3D is the AI-native TypeScript 3D SDK for browser 3D apps, interactive websi
63
63
  - [Aura3D - Slingshot Siege](https://aura3d.auraone.ai/apps/showcase-slingshot-siege/): Aura3D Slingshot Siege prototype: a Saturday-morning-cartoon physics destruction sandbox. Pull the pouch, lob five ammo types, squash every gull, topple every tower.
64
64
  - [Dot Dash 3D — Neon Maze Chase · Aura3D Showcase](https://aura3d.auraone.ai/apps/showcase-dot-dash-3d/): Aura3D Dot Dash 3D showcase, a neon maze-chase arcade game: dash through six glowing circuits gobbling light-dots while four Specters hunt with distinct AI personalities.
65
65
  - [Cross Hop — Aura3D Endless Lane Hopper](https://aura3d.auraone.ai/apps/showcase-cross-hop/): Aura3D Cross Hop showcase, an endless lane-hopping game: dodge cars, trams, trains, rivers and lava across 6 remixed themes, rescue critters, chase the high score.
66
- - [Strange Orbit — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-strange-orbit-port/): Moon-rim bike racing: a seven-lap cup race against five rivals with route splits, checkpoints and a rescue budget, rebuilt from a recorded run of the original. Simulation parity verified against the original's recorded seven-lap run; lit sci-fi look with audio and a difficulty selector, but riders are simple shapes and there is no career or garage. Independent Aura3D reconstruction; not affiliated with the original.
67
- - [Splat Rush — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-splat-rush-port/): Colour-gate ball runner with power-ups, colour switching and a splatter game-over; every simulation field matches the original's recorded run. Audio, effects and keyboard play added; platform SDKs are not ported; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
68
- - [Serpentine Sky Run — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-serpentine-sky-run-port/): Endless runner on a serpentine sky road: eight stages with boost rungs, roll sections and traffic hazards; the outbound run matches a recorded reference. Outbound run only (no hell run or road home); title, pause, results and audio included; the runner is a stand-in character; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
69
- - [Drift &amp;amp; Deliver — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-drift-and-deliver-port/): Arcade drift driving through a block city: coin pickups, 84 traffic cars, bloom and a chase camera; all four recorded scenarios reproduce exactly. Headlight beam renders; art is a look-alike (boxy player car, no animated crowds); collision response untested; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
70
- - [HEATSINK — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-heatsink-port/): Anti-gravity time trial on the Vesper course with boost pads, pickups, barrels and heat management; the state replay matches the original within 1e-6. Simulation port only: the art is visibly different from the original (a blind model judge still told them apart 48 of 48 times, though it rated 38 of 48 pairs as the same game design); Time Trial only, no AI or audio. Independent Aura3D reconstruction; not affiliated with the original.
71
- - [Ninja Runner — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-ninja-runner-port/): Lane-running combat with twelve skinned clones, form changes and a full course win; the recorded 106-second run replays event for event. One rigged character model with tints and props stands in for the original's characters; sun shadows are off; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
72
- - [Last Train to the Sea — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-last-train-port/): Train-driving loop with day and night, passengers, fares and tips; loop length and score match the original's recorded loop and memory stays flat over three loops. No garage or level select; buildings are simple painted boxes and people are capsules; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
73
- - [Roadster Hours — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-roadster-hours-port/): Coastal drive with 92 vehicles in one batched mesh; the recorded 48-second run replays exactly. Coastal look with sky, ocean and a lit road; vehicles are simple models and there are no shadows; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
74
- - [Where the Wind Wanders — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-where-the-wind-wanders-port/): Meadow walk collecting three letters and ringing a bell; the walking trajectory matches the original within 1e-9 over 2,098 steps. Includes the boat epilogue, journal and audio; art is a procedural look-alike; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
75
- - [Silt &amp;amp; Signal — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-silt-and-signal-port/): Free-ride flying over a silt world; three recorded rides reproduce the original's simulation state to 1e-9. Chapter 0 has real colliders; later chapters are generated hills, with no NPCs or minimap; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
76
- - [Cloud Runner — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-cloud-runner-port/): Obstacle runner on a cloud: the first chapter's run model replays the original's recorded run to 1e-12. Includes creatures, boulders, the duel and finale, with audio; the hero is a stand-in character; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
66
+ - [Strange Orbit — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-strange-orbit-port/): Moon-rim bike racing: a seven-lap cup race against five rivals with route splits, checkpoints and a rescue budget, rebuilt from a recorded run of the original. Simulation parity verified against the original's recorded seven-lap run; lit sci-fi look with audio and a difficulty selector; bikes, riders and rim props are generated models (riders are single rigid meshes); no career or garage. Independent Aura3D reconstruction; not affiliated with the original.
67
+ - [Splat Rush — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-splat-rush-port/): Colour-gate ball runner with power-ups, colour switching and a splatter game-over; every simulation field matches the original's recorded run. Audio, effects and keyboard play added; backdrop islands and pickups are generated models; platform SDKs are not ported; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
68
+ - [Serpentine Sky Run — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-serpentine-sky-run-port/): Endless runner on a serpentine sky road: eight stages with boost rungs, roll sections and traffic hazards; the outbound run matches a recorded reference. Outbound run only (no hell run or road home); title, pause, results and audio included; the runner, gate and hazards are generated models; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
69
+ - [Drift &amp;amp; Deliver — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-drift-and-deliver-port/): Arcade drift driving through a block city: coin pickups, 84 traffic cars, bloom and a chase camera; all four recorded scenarios reproduce exactly. Headlight beam renders; the player car, traffic and street furniture are generated models (pedestrians are static); collision response untested; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
70
+ - [HEATSINK — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-heatsink-port/): Anti-gravity time trial on the Vesper course with boost pads, pickups, barrels and heat management; the state replay matches the original within 1e-6. Simulation port only: the drone, pylons and scenery are generated models and the art is still visibly different from the original (a blind model judge picked the original in 35 of 48 trials and rated 40 of 48 pairs as the same game design); Time Trial only, no AI or audio. Independent Aura3D reconstruction; not affiliated with the original.
71
+ - [Ninja Runner — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-ninja-runner-port/): Lane-running combat with twelve skinned clones, form changes and a full course win; the recorded 106-second run replays event for event. four generated rigged characters plus building and prop models; enemies share two bodies; sun shadows are off; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
72
+ - [Last Train to the Sea — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-last-train-port/): Train-driving loop with day and night, passengers, fares and tips; loop length and score match the original's recorded loop and memory stays flat over three loops. No garage or level select; the tram, townhouses, landmarks and passengers are generated models (passengers stand rather than sit); a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
73
+ - [Roadster Hours — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-roadster-hours-port/): Coastal drive with 92 vehicles in one batched mesh; the recorded 48-second run replays exactly. Coastal look with sky, ocean and a lit road; vehicles and scenery are generated models and there are no shadows; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
74
+ - [Where the Wind Wanders — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-where-the-wind-wanders-port/): Meadow walk collecting three letters and ringing a bell; the walking trajectory matches the original within 1e-9 over 2,098 steps. Includes the boat epilogue, journal and audio; the traveller, cottages and trees are generated models; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
75
+ - [Silt &amp;amp; Signal — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-silt-and-signal-port/): Free-ride flying over a silt world; three recorded rides reproduce the original's simulation state to 1e-9. Chapter 0 has real colliders; the rider, ring beacons and landmarks are generated models; later chapters are generated hills, with no NPCs or minimap; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
76
+ - [Cloud Runner — Aura3D port of the mechanics](https://aura3d.auraone.ai/apps/showcase-cloud-runner-port/): Obstacle runner on a cloud: the first chapter's run model replays the original's recorded run to 1e-12. Includes creatures, boulders, the duel and finale, with audio; the hero, creatures and scenery are generated models; a blind model judge could still tell the port from the original. Independent Aura3D reconstruction; not affiliated with the original.
77
77
 
78
78
  ## Notes
79
79
 
@@ -82,7 +82,7 @@ Aura3D is the AI-native TypeScript 3D SDK for browser 3D apps, interactive websi
82
82
  <!-- agent-guide: everything below this marker is the long-form agent guide; gen-seo.mjs preserves it verbatim -->
83
83
  Aura3D agent guide — a typed scene API for building real 3D apps: scenes, looks,
84
84
  lighting, materials, typed assets, camera rigs, gameFeel, SDF text, decals, rigid bodies, and gameplay through
85
- `@aura3d/engine` plus the `@aura3d/cli` toolchain. Current release: Aura3D 4.0.2 (succeeding 4.0.1).
85
+ `@aura3d/engine` plus the `@aura3d/cli` toolchain. Current release: see Facts above (4.1 adds the game kit, QA replay hooks, safety net, audio synth, model loader, GLSL chunks, scatter/merge, racket-sports kits and CLI asset tools).
86
86
 
87
87
  Hello world: every scene gets a look first.
88
88
 
@@ -195,23 +195,32 @@ Mandatory 3D game & scene anti-failure guardrails:
195
195
  - Gate Frame 1 on `await app.ready()` before fading out `#boot-gate` (`opacity: 0; pointer-events: none` then remove) and starting the gameplay simulation loop so lighting, shadow, and GLB silhouettes never pop in mid-frame.
196
196
  - Default `autopilot` / AI demo mode to `false` (immediate manual control on Frame 1 once `#boot-gate` clears); enable AI autopilot only when `?autoplay=1` or `?demo=1` is present in the URL or when the user clicks the HUD `Auto` toggle button, and yield back to manual control on keyboard (`keydown`), pointer, touch, or gamepad input.
197
197
  - Keep mode, stage, camera, `Auto`, and roster selectors in a compact, non-blocking top HUD bar that preserves camera framing, and always call `button.blur()` after button clicks so `Space` or `Enter` gameplay keys never re-trigger focused HUD buttons.
198
- 3. Always decimate and compress Meshy 3D GLBs before admission (`<= 20,000` vertices, `<= 2 MB`, 16-bit indices):
198
+ 3. Always optimize Meshy 3D GLBs before admission (`aura3d assets optimize --file in.glb --out out.glb --role <role>`):
199
199
  - Raw Meshy exports often contain `200,000–1,750,000` vertices and `20–65 MB` of uncompressed 4K PNG textures, which stall main-thread glTF parsing and exhaust GPU memory when multiple models load in one scene.
200
- - Every generated Meshy GLB must be simplified (`MeshoptSimplifier` to `<= 20,000` vertices for props/vehicles, 16-bit `Uint16Array` indices, de-interleaved `byteStride === 0` buffers) and have its textures compressed to `<= 1024x1024` (`~0.5–2.0 MB` per GLB) before being admitted into `public/aura-assets/` so PBR palette, contrast, and reflection stay responsive.
200
+ - Role budgets: prop 6,000, background 15,000, character 30,000, hero or vehicle 40,000 vertices; textures capped at 1024 to 2048 px; 16-bit indices; keep a game's assets near 25 MB. Admit in an isolated project (`assets import-meshy <dir> --into <assets-dir> --prefix <game> --name <role>`), never from the repo root while other work is open.
201
201
  4. Always use canonical typed asset manifest refs (`hash` required):
202
202
  - The production glTF bridge requires `createAssetProvenance(node.asset).source === "typed-aura-assets-manifest"`, which requires a valid 64-character `sha256-...` `hash` and `public/aura-assets/...` provenance from `aura.assets.json` / `src/aura-assets.ts` so the rendered look and silhouette load cleanly.
203
203
  - Never omit `hash` in custom `defineAssets(...)` declarations and never place unhashed duplicate GLBs in route-local `apps/<route>/public/aura-assets/` that collide with root `public/aura-assets/`.
204
204
  5. Mutate node handles instead of calling `app.setScene()` on every restart:
205
205
  - Do not call `app.setScene()` when restarting a round, hole, or bout on the same stage/world; tearing down and remounting the scene disposes live GPU resources, breaks lighting and postprocess continuity, and aborts in-flight GLB loads. Track a scene signature (`stageId` / `worldId`) and reset positions, visibility, and animations through runtime node handles (`app.nodes.require(...)`).
206
- 6. Prevent fog whitewash, overexposure, and instanced emissive blowout (`qualityRebuild.flags`):
207
- - Pass `{ fogDensityScale: 0, exposureEv: -0.1 }` to `looks.preset(...)` and include `qualityRebuild: { flags: ["game", "-compiler", "-post", "-vfxFog"] }` alongside `...looks.appOptions(...)` on fast action/arcade routes unless distance haze is explicitly desired (`looks.preset` emits `density: 24 / far`, which under `A3D_QR_VFX_FOG` creates an `exp2` haze wall at 20–30m). Never stack duplicate high-intensity `lights.directional`, `lights.ambient`, or `lights.hemisphere` nodes on top of `looks.preset(...)`.
208
- - Never set `emissive: "#ffffff"` with high `emissiveIntensity` on an instanced pool material (`instances.box` / `primitives.box({ instances })`): per-instance `instanceColors` multiply `u_baseColor`, not `u_emissiveColor`, so white emissive blows out every instance to pure white regardless of instance color. Always pass an explicit `material: material.pbr({ color })` on untextured preview GLBs.
206
+ 6. Prevent fog whitewash, overexposure, and instanced emissive blowout:
207
+ - If a fast route whitewashes, pass `{ fogDensityScale: 0, exposureEv: -0.1 }` to `looks.preset(...)` for that route. Never pin post-processing, the compiler or fog off for every player with a `qualityRebuild.flags` list: it hides bugs and flattens the look. Install `installSafetyNet` from `@aura3d/debug` so a machine that renders black steps down one rung on its own and `?diag=1` shows the GPU, limits and shader failures.
208
+ - Never set `emissive: "#ffffff"` with high `emissiveIntensity` on an instanced pool material (`instances.box` / `primitives.box({ instances })`): per-instance `instanceColors` multiply `u_baseColor`, not `u_emissiveColor`.
209
209
  7. Ground `model()` feet on platform top surfaces (`y + sy / 2`) & wind custom meshes CCW:
210
210
  - `model(asset, { targetHeight })` normalizes the GLB with `minY = 0` (feet at `position[1]`), whereas `primitives.box` and `primitives.cylinder` (`instances.box` / `instances.cylinder`) are centered on `[-0.5..+0.5]` so a platform at `(y, sy)` has its top surface at `y + sy / 2`. Always place grounded characters and vehicles at `platform.y + platform.sy / 2` so they never sink into logs, pads, or road lanes. Wind custom `primitives.mesh` triangles CCW when viewed from `+Y`.
211
211
  8. Enforce full-viewport `100vw × 100vh` canvas, deep ground apron, and pushbox separation:
212
212
  - Size `html, body, #app, canvas` to `100vw × 100vh` (`position: fixed; inset: 0; overflow: hidden`) and extend the 3D ground/stage apron deep enough in `+Z`/`-Z` under the camera frustum so the bottom third of the viewport never shows a black clear-color void. Disable `collision: true` on `camera.rigs.shoulder` when the target character has attached child runtime nodes, and enforce `>= 1.4m` fighter pushbox separation (`|x| >= 1.8m` at spawn).
213
213
 
214
214
 
215
+ Aura3D 4.1 kits (build the shell from these, do not hand-roll it):
216
+
217
+ - `@aura3d/engine/game`: `createGame` (lifecycle, pause, results, DOM HUD, touch, juice, game sound); the root `createGame` is a headless stub.
218
+ - `@aura3d/engine`: `createFixedStepLoop`, `installQaHooks` (`window.__AURA_<NAME>__`), `readQaParams`, `createCountedRng` for deterministic replay and automated checks; `defineModelAssets` for hashed same-origin models with a `?models=0` procedural fallback; `scatterAlongPath`, `scatterInArea`, `splitScatterByDistance`, `mergeStatic` for draw-call budgets; `game.racketSports()` with `ballFlight`, `scoring`, `athleteRig`.
219
+ - `@aura3d/engine/audio`: `createCueBank`, `createStepSequencer`, `createToneLoop`, `createSampleSlices` on the single `AudioContextManager` (never `new AudioContext(`).
220
+ - `@aura3d/debug`: `installSafetyNet` (`?diag=1`, quality switches `?post=0 ?shadow=0 ?env=0 ?sky=0 ?quality=low`, black-frame self-heal).
221
+ - `material.custom` shaders: `#include <a3d_math>`, `<a3d_noise>`, `<a3d_sky>`, `<a3d_fog>`, `<a3d_triplanar>`, `<a3d_toon>`, `<colorspace_decode>`; custom shaders write linear light, so use `a3dSrgbToLinear` with `toneMapping: "none"` for authored colours, and keep `pow`, `normalize`, `sqrt` and divides NaN-safe (Apple GPUs return NaN where software renderers do not).
222
+ - Production checks that CI missed: the bridge refuses http(s) or unhashed model URLs and then draws nothing; instanced and batched meshes honour texture maps; empty instance arrays are no-ops; software-GL passes are not real-GPU proof. Docs: `docs/api/game-kit.md`, `qa-kit.md`, `safety-net.md`, `audio-synth.md`, `model-loader.md`, `glsl-chunks.md`, `scatter-merge.md`, `cli-asset-tools.md`; pitfalls: `aura3d-core/references/pitfalls.md`.
223
+
215
224
  Claims: use every feature freely. Claim only what a judged capture shows.
216
225
 
217
226
  - Label every capability as one of: `createAuraApp` root safe API,
@@ -241,3 +250,4 @@ label the result `prototype`.
241
250
 
242
251
 
243
252
 
253
+
@@ -80,11 +80,9 @@ when a catalog candidate already passes the rubric.
80
80
  often contain `200,000–1,750,000` vertices and `20–65 MB` of uncompressed 4K
81
81
  PNG textures, which stall main-thread glTF parsing and exhaust GPU memory
82
82
  when multiple models share one scene look and lighting setup. Before
83
- admission into `public/aura-assets/`, always simplify every generated Meshy
84
- GLB (`MeshoptSimplifier` to `<= 20,000` vertices for props/vehicles, 16-bit
85
- `Uint16Array` indices, de-interleaved `byteStride === 0` buffers) and
86
- compress textures to `<= 1024x1024` (`~0.5–2.0 MB` per GLB) so silhouettes,
87
- shadows, palette, and PBR reflections load fast.
83
+ admission run `assets optimize --file in.glb --out out.glb --role prop` on
84
+ every generated GLB (role vertex budgets, textures `<= 1024–2048` px,
85
+ float32 geometry kept; see [asset-tooling](references/asset-tooling.md)).
88
86
  7. Admit the local output with rights evidence.
89
87
 
90
88
  ```bash
@@ -93,6 +91,10 @@ when a catalog candidate already passes the rubric.
93
91
  --profile prop --role prop --thumbnail artifacts/meshy/arena-relic/thumbnail.png
94
92
  ```
95
93
 
94
+ Use `--into <game-assets> --prefix <game> --name <role>` for isolated import
95
+ (default in the monorepo; no shared root manifests are written). Merge each
96
+ `animate` clip into one rig with `assets merge-clips`, and reserve every paid
97
+ call with `meshy ledger`: see [asset-tooling](references/asset-tooling.md).
96
98
  Use `--file` when the run has several GLBs. Import defaults to
97
99
  `--quality candidate` and refuses release certification. Read the
98
100
  admission `blockers` and `unproven` lists; `routeReady: false` is a result
@@ -0,0 +1,72 @@
1
+ # Asset tooling for Meshy work
2
+
3
+ Detail for the `meshy-cli` skill: optimise, merge clips, admit in isolation, and keep a credit ledger.
4
+ None of these commands call Meshy or handle a credential; they work on files you already downloaded.
5
+
6
+ ## Optimise a download
7
+
8
+ ```bash
9
+ npx @aura3d/cli@latest assets optimize --file artifacts/meshy/drift-lamp/model.glb \
10
+ --out artifacts/meshy/drift-lamp/model.opt.glb --role prop
11
+ ```
12
+
13
+ Role budgets (vertices, texture px): `hero` and `vehicle` 40,000 / 2048, `character` 30,000 / 2048,
14
+ `background` 15,000 / 1024, `prop` 6,000 / 1024. Override with `--max-verts N` and `--tex N`
15
+ (max 4096), skip simplification with `--no-simplify`. The command dedups, welds, simplifies to the
16
+ budget (locking borders on skinned meshes), prunes, downsizes textures (colour and ORM to jpeg,
17
+ normal to png) and never overwrites its input. Geometry stays float32 unless you pass `--meshopt`:
18
+ quantisation moves scale into a node transform and skews the grounding and bounds checks that
19
+ `import-meshy` runs, so use it only for static background props you will not measure.
20
+ The glTF-Transform stack resolves from your project; a missing package is reported with the install command.
21
+
22
+ ## Merge animation clips into one rig
23
+
24
+ Each `animate` task returns its own GLB with one clip on the rig's skeleton. Merge them by node name:
25
+
26
+ ```bash
27
+ npx @aura3d/cli@latest assets merge-clips --base artifacts/meshy/hero-rig/ \
28
+ --out artifacts/meshy/hero/model.glb \
29
+ run=artifacts/meshy/hero-run/ idle=artifacts/meshy/hero-idle/ jump=artifacts/meshy/hero-jump/
30
+ ```
31
+
32
+ `--base` and each clip accept a GLB or a Meshy download directory (`rigged_character_glb.glb`,
33
+ `animation_glb.glb`). A clip whose channels match no node of the base rig is rejected (different
34
+ skeleton) instead of producing a silent T-pose; unmatched channels are counted per clip. The rest
35
+ poses the rig task shipped with are dropped unless you pass `--keep-base-animations`.
36
+
37
+ ## Admit in isolation
38
+
39
+ Inside the Aura3D monorepo, `assets import-meshy` runs isolated by default because a plain import
40
+ rewrites shared root files (`aura.assets.json`, `src/aura-assets.ts`, `public/aura-assets/*`,
41
+ `aura-evidence/*`) and many games importing at once race. Isolated mode admits in a temporary project,
42
+ validates there, and copies only the hashed GLB and its evidence into the game's folder:
43
+
44
+ ```bash
45
+ npx @aura3d/cli@latest assets import-meshy artifacts/meshy/drift-lamp/ \
46
+ --into examples/drift-port/assets --prefix drift --name lamp \
47
+ --rights-evidence artifacts/meshy/drift-lamp/rights.json --profile prop
48
+ ```
49
+
50
+ The typed key is `drift_lamp`; files land in `models/drift_lamp.glb` and
51
+ `provenance/drift_lamp.rights.json` and `.meta.json`. A different model already at that path is refused
52
+ unless `--overwrite`; `--keep-temp` keeps the temporary project for debugging; `--shared-manifest` opts out
53
+ and writes the current project's manifests. Name raw downloads with the game prefix (`drift-lamp`), never
54
+ a bare `rocks` or `board`: two games sharing one `rocks.raw.glb` silently reuse each other's model.
55
+
56
+ ## Credit ledger
57
+
58
+ ```bash
59
+ rid=$(npx @aura3d/cli@latest meshy ledger reserve --game drift --op preview --cap 800 --global-cap 10000)
60
+ npx @aura3d/cli@latest meshy ledger settle --rid "$rid" --exit-code 0 --task-id <id>
61
+ npx @aura3d/cli@latest meshy ledger status
62
+ ```
63
+
64
+ The ledger (`--ledger path` or `AURA3D_MESHY_LEDGER`, default `~/.aura3d/meshy-ledger.jsonl`) enforces a
65
+ per-game cap and one global cap under a lock, so concurrent agents cannot overspend. `--game` is required
66
+ and must be a lowercase tag. Estimates: preview 20, refine 10, remesh 5, rigging 5, animate 3, retexture 10.
67
+
68
+ A rate-limited (exit 6) or timed-out (exit 8) call is NOT a failure: the task usually finishes and is
69
+ charged. `settle --exit-code` records it as `unconfirmed`, which keeps counting its estimate. Resume the
70
+ task with `meshy <resource> get <id>` instead of paying again, then
71
+ `meshy ledger reconcile --rid <id> --status ok` (or `failed`). Only a confirmed no-charge outcome (auth
72
+ exit 3, validation exit 4, credits exit 9, or a FAILED task) settles as `failed` and counts 0.