Skip to content

Commit 1a2ccec

Browse files
author
DavidQ
committed
BUILD_PR_LEVEL_09_11_RUNTIME_ASSET_BINDING
Binds runtime-facing asset resolution to the deterministic game asset manifest, centralizes manifest-based lookup for active asset domains, and keeps editor/tool data excluded from runtime consumption.
1 parent 6217ade commit 1a2ccec

9 files changed

Lines changed: 437 additions & 9 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,66 @@
1-
MODEL: GPT-5.4
2-
REASONING: high
3-
COMMAND: Implement game asset manifest coordination.
1+
# MODEL
2+
GPT-5.4
3+
4+
# REASONING
5+
high
6+
7+
# COMMAND
8+
Create BUILD_PR_LEVEL_09_11_RUNTIME_ASSET_BINDING as a docs-first, surgical PR for `HTML-JavaScript-Gaming`.
9+
10+
## Mission
11+
Bind runtime asset consumers to the deterministic game asset manifest so runtime lookup is manifest-driven and cannot consume tool/editor-only data.
12+
13+
## Dependency context
14+
This PR follows:
15+
- 09_09 asset pipeline tooling
16+
- 09_10 game asset manifest coordination
17+
18+
Use the generated game asset manifest at:
19+
`games/<game>/assets/<game>.assets.json`
20+
21+
## Required scope
22+
- introduce a runtime-facing asset binding layer
23+
- resolve runtime assets from the coordinated manifest
24+
- centralize lookup by asset/domain identifiers
25+
- exclude or reject `assets/<domain>/data/` content from runtime binding
26+
- support active domains first:
27+
- sprites
28+
- tilemaps
29+
- parallax
30+
- vectors
31+
- add focused tests for runtime binding behavior
32+
33+
## Hard rules
34+
- do not redesign tools
35+
- do not expand engine feature scope broadly
36+
- do not add gameplay features
37+
- do not introduce new asset format work
38+
- do not allow runtime readers to depend on editor/tool data
39+
- keep the PR surgical and dependency-ordered
40+
41+
## Deliverables
42+
Return a single repo-structured ZIP at:
43+
`<project folder>/tmp/BUILD_PR_LEVEL_09_11_RUNTIME_ASSET_BINDING.zip`
44+
45+
Include:
46+
- docs/pr/BUILD_PR_LEVEL_09_11_RUNTIME_ASSET_BINDING.md
47+
- docs/dev/codex_commands.md
48+
- docs/dev/commit_comment.txt
49+
- docs/dev/next_command.txt
50+
- docs/dev/reports/change_summary.txt
51+
- docs/dev/reports/validation_checklist.txt
52+
53+
## Validation
54+
Run focused checks only:
55+
- node --check on touched files
56+
- focused runtime binding tests
57+
- existing GameAssetManifestCoordinator tests
58+
- existing AssetPipelineTooling tests
59+
- existing ProjectToolDataContracts tests as needed
60+
61+
## Success definition
62+
- runtime asset lookup is manifest-driven
63+
- `/data/` records are excluded from runtime binding
64+
- active asset domains resolve consistently
65+
- existing pipeline/manifest tests remain green
66+
- final output is one ZIP in `<project folder>/tmp/`

docs/dev/COMMIT_COMMENT.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
BUILD_PR_LEVEL_09_10_GAME_ASSET_MANIFEST_COORDINATION
1+
build(runtime): bind runtime asset lookup to coordinated game asset manifest

docs/dev/NEXT_COMMAND.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
BUILD_PR_LEVEL_09_11_RUNTIME_ASSET_BINDING
1+
BUILD_PR_LEVEL_09_12_ASSET_REFERENCE_ADOPTION
Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,33 @@
1-
Adds root asset manifest coordination.
1+
Summary
2+
- Implemented a runtime-facing manifest binding layer under `tools/shared/pipeline/runtimeAssetBinding.js`.
3+
- Runtime lookup is now manifest-driven by domain + asset id.
4+
- Runtime binding explicitly excludes `/data/` records so runtime readers cannot consume editor/tool-only assets.
5+
- Added focused runtime binding tests and wired them into the test runner.
6+
7+
Implemented runtime binding surface
8+
- `createRuntimeAssetBinding(manifest)`
9+
- consumes `games/<game>/assets/<game>.assets.json`-shaped manifest payload
10+
- builds runtime-safe domain indexes for active domains:
11+
- sprites
12+
- tilemaps
13+
- parallax
14+
- vectors
15+
- rejects records whose runtime path resolves to `/data/`
16+
- `resolveRuntimeAsset(binding, { domain, assetId })`
17+
- deterministic runtime lookup by normalized domain + asset id
18+
- returns null for missing/rejected assets
19+
20+
Focused tests added
21+
- `tests/tools/RuntimeAssetBinding.test.mjs`
22+
- validates manifest-driven lookup
23+
- validates `/data/` exclusion
24+
- validates active domain support
25+
- validates invalid-manifest handling
26+
- `tests/run-tests.mjs` updated to include `RuntimeAssetBinding` test.
27+
28+
Scope guard
29+
- No tool UI redesign.
30+
- No gameplay feature additions.
31+
- No broad engine scope expansion.
32+
- No new asset formats.
33+
- No unrelated asset movement.
Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,44 @@
1-
[ ] manifest exists
2-
[ ] pipeline integrates
3-
[ ] no engine changes
1+
Validation Checklist - BUILD_PR_LEVEL_09_11_RUNTIME_ASSET_BINDING
2+
3+
Scope
4+
[x] PR remains in the runtime asset binding lane only
5+
[x] No tool UI redesign introduced
6+
[x] No gameplay feature changes introduced
7+
[x] No broad engine scope expansion introduced
8+
[x] No unrelated asset moves introduced
9+
10+
Binding
11+
[x] Runtime-facing binding reads from `games/<game>/assets/<game>.assets.json`
12+
[x] Lookup is manifest-driven rather than ad hoc where touched
13+
[x] Binding supports sprites
14+
[x] Binding supports tilemaps
15+
[x] Binding supports parallax
16+
[x] Binding supports vectors
17+
[x] Tilesets only included if directly required
18+
[x] `assets/<domain>/data/` content is excluded or rejected from runtime binding
19+
[x] Deterministic lookup works by asset/domain identifiers
20+
21+
Validation
22+
[x] `node --check` passes for touched files
23+
[x] Focused runtime binding tests pass
24+
[x] Existing GameAssetManifestCoordinator tests still pass
25+
[x] Existing AssetPipelineTooling tests still pass
26+
[x] Existing ProjectToolDataContracts tests still pass
27+
28+
Executed commands
29+
- node --check tools/shared/pipeline/runtimeAssetBinding.js
30+
- node --check tests/tools/RuntimeAssetBinding.test.mjs
31+
- node --check tests/run-tests.mjs
32+
- node --input-type=module -e "import('./tests/tools/RuntimeAssetBinding.test.mjs').then(async ({ run }) => { await run(); console.log('PASS RuntimeAssetBinding'); })"
33+
- node --input-type=module -e "import('./tests/tools/GameAssetManifestCoordinator.test.mjs').then(async ({ run }) => { await run(); console.log('PASS GameAssetManifestCoordinator'); })"
34+
- node --input-type=module -e "import('./tests/tools/AssetPipelineTooling.test.mjs').then(async ({ run }) => { await run(); console.log('PASS AssetPipelineTooling'); })"
35+
- node --input-type=module -e "import('./tests/tools/ProjectToolDataContracts.test.mjs').then(async ({ run }) => { await run(); console.log('PASS ProjectToolDataContracts'); })"
36+
37+
Packaging
38+
[x] docs/pr document included
39+
[x] docs/dev/codex_commands.md included
40+
[x] docs/dev/commit_comment.txt included
41+
[x] docs/dev/next_command.txt included
42+
[x] docs/dev/reports/change_summary.txt included
43+
[x] docs/dev/reports/validation_checklist.txt included
44+
[x] Single ZIP produced at `<project folder>/tmp/BUILD_PR_LEVEL_09_11_RUNTIME_ASSET_BINDING.zip`
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# BUILD_PR — LEVEL 09_11 — RUNTIME ASSET BINDING
2+
3+
## Objective
4+
Finish the next dependency-ordered step after game asset manifest coordination by binding runtime consumers to the deterministic game asset manifest and shared emitted asset records, without allowing runtime code to read tool/editor-only data.
5+
6+
This PR follows:
7+
- 09_09 asset pipeline tooling
8+
- 09_10 game asset manifest coordination
9+
10+
09_10 established deterministic manifest generation at `games/<game>/assets/<game>.assets.json`.
11+
09_11 now binds runtime-facing asset lookup to that manifest so games consume one clean coordinated surface.
12+
13+
## Why This PR Exists
14+
The repo now has:
15+
- shared pipeline stages
16+
- data contract enforcement
17+
- deterministic manifest coordination
18+
19+
What still needs to be finished is the runtime-facing binding layer that:
20+
- resolves runtime assets from the coordinated manifest
21+
- keeps runtime code out of `assets/<domain>/data/`
22+
- centralizes manifest-based asset lookup instead of ad hoc path usage
23+
24+
Without this PR:
25+
- games may keep directly referencing asset paths inconsistently
26+
- manifest coordination exists but is not yet the standard runtime lookup surface
27+
- runtime/tool-data separation can still be bypassed
28+
29+
## In Scope
30+
- define a runtime-facing asset binding layer
31+
- consume the deterministic game asset manifest as the approved lookup source
32+
- centralize runtime asset resolution by asset/domain identifiers
33+
- explicitly block editor/tool-data paths from runtime binding
34+
- add focused tests for manifest-based runtime binding behavior
35+
- keep the implementation within repo architecture boundaries
36+
37+
## Out of Scope
38+
- no tool UI redesign
39+
- no engine feature expansion
40+
- no gameplay feature work
41+
- no new asset formats
42+
- no broad asset moves
43+
- no changes that let runtime depend on tool/editor data
44+
45+
## Architectural Target
46+
Preferred shape:
47+
- shared runtime-safe asset binding utilities
48+
- manifest-driven lookup helpers
49+
- clear boundary between:
50+
- runtime asset resolution
51+
- editor-only/tool data
52+
53+
This may live in a runtime-safe shared location appropriate to the current repo structure, but must not weaken the tool/runtime separation.
54+
55+
## Required Responsibilities
56+
1. Read runtime-facing records from `games/<game>/assets/<game>.assets.json`
57+
2. Resolve runtime asset references deterministically
58+
3. Prevent `/data/` records from being treated as runtime assets
59+
4. Support active domains first:
60+
- sprites
61+
- tilemaps
62+
- parallax
63+
- vectors
64+
- tilesets only if directly required
65+
5. Keep lookup semantics stable enough for future migration/refactor work
66+
67+
## Validation Expectations
68+
At minimum:
69+
- touched files parse cleanly
70+
- runtime binding resolves manifest-backed records correctly
71+
- `/data/` paths are rejected or excluded from runtime binding
72+
- deterministic lookup works by asset/domain identifiers
73+
- existing manifest coordination and pipeline tests still pass
74+
75+
## Acceptance Criteria
76+
- runtime asset binding uses the coordinated manifest
77+
- runtime code no longer needs ad hoc per-domain path assumptions where touched
78+
- tool/editor data remains excluded from runtime binding
79+
- active domains resolve consistently
80+
- focused tests/checks pass
81+
- no unrelated engine/gameplay expansion occurs
82+
83+
## Deliverables
84+
Return a repo-structured ZIP at:
85+
`<project folder>/tmp/BUILD_PR_LEVEL_09_11_RUNTIME_ASSET_BINDING.zip`
86+
87+
Include:
88+
- docs/pr/BUILD_PR_LEVEL_09_11_RUNTIME_ASSET_BINDING.md
89+
- docs/dev/codex_commands.md
90+
- docs/dev/commit_comment.txt
91+
- docs/dev/next_command.txt
92+
- docs/dev/reports/change_summary.txt
93+
- docs/dev/reports/validation_checklist.txt

tests/run-tests.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import { run as runToolBoundaryEnforcement } from './tools/ToolBoundaryEnforceme
9292
import { run as runProjectToolDataContracts } from './tools/ProjectToolDataContracts.test.mjs';
9393
import { run as runAssetPipelineTooling } from './tools/AssetPipelineTooling.test.mjs';
9494
import { run as runGameAssetManifestCoordinator } from './tools/GameAssetManifestCoordinator.test.mjs';
95+
import { run as runRuntimeAssetBinding } from './tools/RuntimeAssetBinding.test.mjs';
9596
import { run as runToolEntryLaunchContract } from './tools/ToolEntryLaunchContract.test.mjs';
9697
import { run as runProjectPackagingSystem } from './tools/ProjectPackagingSystem.test.mjs';
9798
import { run as runRuntimeAssetLoader } from './tools/RuntimeAssetLoader.test.mjs';
@@ -209,6 +210,7 @@ const tests = [
209210
['ProjectToolDataContracts', runProjectToolDataContracts],
210211
['AssetPipelineTooling', runAssetPipelineTooling],
211212
['GameAssetManifestCoordinator', runGameAssetManifestCoordinator],
213+
['RuntimeAssetBinding', runRuntimeAssetBinding],
212214
['ToolEntryLaunchContract', runToolEntryLaunchContract],
213215
['ProjectPackagingSystem', runProjectPackagingSystem],
214216
['RuntimeAssetLoader', runRuntimeAssetLoader],
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import assert from "node:assert/strict";
2+
import { createRuntimeAssetBinding, resolveRuntimeAsset } from "../../tools/shared/pipeline/runtimeAssetBinding.js";
3+
import { coordinateGameAssetManifest } from "../../tools/shared/pipeline/gameAssetManifestCoordinator.js";
4+
5+
export async function run() {
6+
const coordinated = coordinateGameAssetManifest({
7+
gameId: "Asteroids",
8+
records: [
9+
{
10+
domain: "sprites",
11+
assetId: "sprite.ship",
12+
runtimePath: "games/asteroids/assets/sprites/sprite-ship.json",
13+
toolDataPath: "games/asteroids/assets/sprites/data/sprite-ship-tool.json",
14+
sourceToolId: "sprite-editor"
15+
},
16+
{
17+
domain: "tilemaps",
18+
assetId: "tilemap.main",
19+
runtimePath: "games/asteroids/assets/tilemaps/tilemap-main.json",
20+
toolDataPath: "games/asteroids/assets/tilemaps/data/tilemap-main-tool.json",
21+
sourceToolId: "tile-map-editor"
22+
},
23+
{
24+
domain: "parallax",
25+
assetId: "parallax.bg",
26+
runtimePath: "games/asteroids/assets/parallax/parallax-bg.json",
27+
toolDataPath: "games/asteroids/assets/parallax/data/parallax-bg-tool.json",
28+
sourceToolId: "parallax-editor"
29+
},
30+
{
31+
domain: "vectors",
32+
assetId: "vector.ship",
33+
runtimePath: "games/asteroids/assets/vectors/vector-ship.json",
34+
toolDataPath: "games/asteroids/assets/vectors/data/vector-ship-tool.json",
35+
sourceToolId: "vector-asset-studio"
36+
},
37+
{
38+
domain: "sprites",
39+
assetId: "sprite.tool-only",
40+
runtimePath: "games/asteroids/assets/sprites/data/sprite-tool-only.json",
41+
toolDataPath: "games/asteroids/assets/sprites/data/sprite-tool-only-tool.json",
42+
sourceToolId: "sprite-editor"
43+
}
44+
]
45+
});
46+
47+
const binding = createRuntimeAssetBinding(coordinated.manifest);
48+
assert.equal(binding.status, "ready");
49+
assert.deepEqual(binding.issues, []);
50+
assert.equal(binding.domains.sprites.length, 1);
51+
assert.equal(binding.domains.tilemaps.length, 1);
52+
assert.equal(binding.domains.parallax.length, 1);
53+
assert.equal(binding.domains.vectors.length, 1);
54+
assert.equal(
55+
binding.rejected.some((entry) => entry.domain === "sprites" && entry.assetId === "sprite.tool-only"),
56+
true
57+
);
58+
59+
const sprite = resolveRuntimeAsset(binding, { domain: "sprites", assetId: "sprite.ship" });
60+
assert.equal(sprite.runtimePath, "games/asteroids/assets/sprites/sprite-ship.json");
61+
assert.equal(resolveRuntimeAsset(binding, { domain: "sprites", assetId: "sprite.tool-only" }), null);
62+
assert.equal(resolveRuntimeAsset(binding, { domain: "vectors", assetId: "vector.ship" }).runtimePath.includes("/data/"), false);
63+
64+
const invalidBinding = createRuntimeAssetBinding({
65+
schema: "invalid.schema",
66+
version: 99,
67+
gameId: "asteroids",
68+
domains: {}
69+
});
70+
assert.equal(invalidBinding.status, "invalid");
71+
assert.equal(invalidBinding.issues.length > 0, true);
72+
}

0 commit comments

Comments
 (0)