Skip to content

Commit 1cfc4cb

Browse files
author
DavidQ
committed
docs: add BUILD_PR project-level tool integration bundle
1 parent 0d09c1b commit 1cfc4cb

9 files changed

+410
-18
lines changed

docs/dev/CODEX_COMMANDS.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ MODEL: GPT-5.4
22
REASONING: high
33

44
COMMAND:
5-
Create BUILD_PR_TOOL_HOST_STATE_HANDOFF
5+
Create BUILD_PR_PROJECT_TOOL_INTEGRATION
66

77
Scope:
8-
- add shared context for tools
9-
- allow optional state passing
10-
- minimal changes
8+
- unify project model across tools
9+
- normalize asset references
10+
- minimal adapters
1111

1212
Validation:
1313
- npm run test:launch-smoke -- --tools
14-
- verify state handoff works
14+
- verify project works across tools
1515

1616
Output:
17-
<project>/tmp/BUILD_PR_TOOL_HOST_STATE_HANDOFF_delta.zip
17+
<project>/tmp/BUILD_PR_PROJECT_TOOL_INTEGRATION_delta.zip

docs/dev/COMMIT_COMMENT.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
docs: add BUILD_PR tool host state handoff bundle
1+
docs: add BUILD_PR project-level tool integration bundle
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# BUILD_PR_PROJECT_TOOL_INTEGRATION Report
2+
3+
## Scope Outcome
4+
- Unified project-level tool integration model in shared project manifest flow.
5+
- Normalized cross-tool asset references into one aggregated integration block.
6+
- Kept changes adapter-focused and minimal in `tools/shared` only.
7+
8+
## Implementation Summary
9+
- Added shared integration helper:
10+
- `tools/shared/projectToolIntegration.js`
11+
- Normalizes tool states for manifest persistence.
12+
- Extracts tool asset references into a normalized structure.
13+
- Builds `toolIntegration` block with per-tool and aggregate asset references.
14+
- Updated manifest contract:
15+
- `tools/shared/projectManifestContract.js`
16+
- Sanitizes persisted tool states with integration helper.
17+
- Always creates/migrates `toolIntegration` from `tools` block.
18+
- Validates presence of `toolIntegration`.
19+
- Updated project system controller:
20+
- `tools/shared/projectSystem.js`
21+
- Normalizes captured/default/opened tool states before persistence.
22+
- Unwraps compatible state shape before adapter apply.
23+
- Rebuilds `toolIntegration` during project lifecycle transitions.
24+
25+
## Validation
26+
- `npm run test:launch-smoke -- --tools`
27+
- PASS (`9/9` tools)
28+
- Cross-tool project integration verification (module-level check):
29+
- Built mixed-tool manifest payload (tilemap/parallax/sprite/palette/asset browser).
30+
- Confirmed normalized per-tool refs in persisted tool states.
31+
- Confirmed aggregated `toolIntegration.assetReferences` includes normalized IDs across tools.
32+
- Result: PASS.
33+
34+
## Files Changed
35+
- `tools/shared/projectToolIntegration.js`
36+
- `tools/shared/projectManifestContract.js`
37+
- `tools/shared/projectSystem.js`
38+
- `docs/dev/reports/launch_smoke_report.md`
39+
- `docs/dev/reports/BUILD_PR_PROJECT_TOOL_INTEGRATION_report.md`

docs/dev/reports/launch_smoke_report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Launch Smoke Report
22

3-
Generated: 2026-04-11T23:53:57.877Z
3+
Generated: 2026-04-12T00:01:45.164Z
44

55
Filters: games=false, samples=false, tools=true, sampleRange=all
66

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
PROJECT TOOL INTEGRATION TARGETS
2+
3+
- shared project schema
4+
- asset reference normalization
5+
- cross-tool project loading
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# BUILD_PR_PROJECT_TOOL_INTEGRATION
2+
3+
## Purpose
4+
Unify project-level data across tools so assets, maps, and scenes can be shared consistently.
5+
6+
## Goals
7+
- single project model
8+
- shared asset references
9+
- cross-tool compatibility
10+
11+
## Scope
12+
- project schema normalization
13+
- asset reference alignment
14+
- adapter layer for tools
15+
16+
## Out of Scope
17+
- tool UI redesign
18+
- rendering changes
19+
- deep editor refactors
20+
21+
## Strategy
22+
- leverage tools/shared/projectSystem
23+
- align asset paths and IDs
24+
- minimal adapters per tool
25+
26+
## Validation
27+
- npm run test:launch-smoke -- --tools
28+
- open project across multiple tools
29+
- no console errors

tools/shared/projectManifestContract.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { readSharedAssetHandoff, readSharedPaletteHandoff } from "./assetUsageIntegration.js";
22
import { cloneValue, safeString } from "./projectSystemValueUtils.js";
3+
import {
4+
buildProjectToolIntegration,
5+
PROJECT_TOOL_INTEGRATION_SCHEMA,
6+
PROJECT_TOOL_INTEGRATION_VERSION,
7+
normalizeToolStateForProjectManifest
8+
} from "./projectToolIntegration.js";
39

410
export const PROJECT_MANIFEST_SCHEMA = "html-js-gaming.project";
511
export const PROJECT_MANIFEST_VERSION = 1;
@@ -39,7 +45,7 @@ function sanitizeToolsBlock(rawTools) {
3945
if (typeof toolId !== "string" || !toolId.trim() || !value || typeof value !== "object") {
4046
return;
4147
}
42-
tools[toolId.trim()] = cloneValue(value);
48+
tools[toolId.trim()] = normalizeToolStateForProjectManifest(toolId.trim(), value);
4349
});
4450
return tools;
4551
}
@@ -88,6 +94,7 @@ export function normalizeProjectFileName(projectName) {
8894
export function createEmptyProjectManifest(options = {}) {
8995
const now = new Date().toISOString();
9096
const toolId = sanitizeString(options.toolId, "");
97+
const tools = sanitizeToolsBlock(options.tools);
9198
const name = sanitizeString(options.name, "Untitled Project");
9299
const manifest = {
93100
schema: PROJECT_MANIFEST_SCHEMA,
@@ -103,7 +110,8 @@ export function createEmptyProjectManifest(options = {}) {
103110
palette: sanitizeSharedReference(options.sharedReferences?.palette || readSharedPaletteHandoff())
104111
},
105112
sharedLibrary: sanitizeSharedLibrary(options.sharedLibrary),
106-
tools: sanitizeToolsBlock(options.tools),
113+
tools,
114+
toolIntegration: buildProjectToolIntegration(tools),
107115
workspace: {
108116
lastOpenTool: toolId,
109117
notes: sanitizeString(options.workspace?.notes, "")
@@ -149,6 +157,12 @@ export function migrateProjectManifest(rawManifest) {
149157
migrated.migration.applied.push("forward-version-opened-as-compatible");
150158
}
151159

160+
if (!rawManifest.toolIntegration || typeof rawManifest.toolIntegration !== "object") {
161+
migrated.migration.applied.push("project-tool-integration-created");
162+
}
163+
164+
migrated.toolIntegration = buildProjectToolIntegration(migrated.tools);
165+
152166
return migrated;
153167
}
154168

@@ -177,6 +191,17 @@ export function validateProjectManifest(rawManifest) {
177191
issues.push("Project manifest tools block is required.");
178192
}
179193

194+
if (!manifest.toolIntegration || typeof manifest.toolIntegration !== "object") {
195+
issues.push("Project manifest toolIntegration block is required.");
196+
} else {
197+
if (manifest.toolIntegration.schema !== PROJECT_TOOL_INTEGRATION_SCHEMA) {
198+
warnings.push(`Project toolIntegration schema expected ${PROJECT_TOOL_INTEGRATION_SCHEMA} but received ${manifest.toolIntegration.schema || "unknown"}.`);
199+
}
200+
if (manifest.toolIntegration.version !== PROJECT_TOOL_INTEGRATION_VERSION) {
201+
warnings.push(`Project toolIntegration version expected ${PROJECT_TOOL_INTEGRATION_VERSION} but received ${manifest.toolIntegration.version || "unknown"}.`);
202+
}
203+
}
204+
180205
if (manifest.sharedReferences.asset && !manifest.sharedReferences.asset.id && !manifest.sharedReferences.asset.sourcePath) {
181206
warnings.push("Shared asset reference is present but does not include an id or source path.");
182207
}

tools/shared/projectSystem.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import {
1010
} from "./projectManifestContract.js";
1111
import { getProjectAdapter } from "./projectSystemAdapters.js";
1212
import { cloneValue, safeString } from "./projectSystemValueUtils.js";
13+
import {
14+
buildProjectToolIntegration,
15+
normalizeToolStateForProjectManifest,
16+
unwrapToolStateForAdapter
17+
} from "./projectToolIntegration.js";
1318

1419
function readStorage() {
1520
try {
@@ -87,15 +92,23 @@ export function createProjectSystemController(options = {}) {
8792
currentManifest.workspace.lastOpenTool = toolId;
8893
currentManifest.updatedAt = new Date().toISOString();
8994
currentManifest.sharedReferences = captureSharedReferenceSnapshot();
95+
currentManifest.tools = currentManifest.tools && typeof currentManifest.tools === "object"
96+
? currentManifest.tools
97+
: {};
9098

9199
if (toolAdapter.ready) {
92-
currentManifest.tools[toolId] = toolAdapter.captureState();
100+
currentManifest.tools[toolId] = normalizeToolStateForProjectManifest(
101+
toolId,
102+
toolAdapter.captureState()
103+
);
93104
const adapterName = safeString(toolAdapter.getProjectName?.(), "");
94105
if (adapterName) {
95106
currentManifest.name = adapterName;
96107
}
97108
}
98109

110+
currentManifest.toolIntegration = buildProjectToolIntegration(currentManifest.tools);
111+
99112
return migrateProjectManifest(currentManifest);
100113
}
101114

@@ -156,7 +169,7 @@ export function createProjectSystemController(options = {}) {
156169
const manifest = ensureProjectManifest();
157170
const toolState = manifest.tools?.[toolId];
158171
if (toolState) {
159-
toolAdapter.applyState(cloneValue(toolState));
172+
toolAdapter.applyState(cloneValue(unwrapToolStateForAdapter(toolId, toolState)));
160173
onStatus(buildStatusSummary(validateProjectManifest(manifest)));
161174
}
162175
state.appliedInitialState = true;
@@ -172,8 +185,10 @@ export function createProjectSystemController(options = {}) {
172185
toolId
173186
});
174187
if (toolAdapter.ready) {
175-
nextManifest.tools[toolId] = toolAdapter.createDefaultState(nextName);
176-
toolAdapter.applyState(cloneValue(nextManifest.tools[toolId]));
188+
const defaultState = normalizeToolStateForProjectManifest(toolId, toolAdapter.createDefaultState(nextName));
189+
nextManifest.tools[toolId] = defaultState;
190+
nextManifest.toolIntegration = buildProjectToolIntegration(nextManifest.tools);
191+
toolAdapter.applyState(cloneValue(unwrapToolStateForAdapter(toolId, defaultState)));
177192
}
178193
state.manifest = nextManifest;
179194
state.appliedInitialState = true;
@@ -192,9 +207,12 @@ export function createProjectSystemController(options = {}) {
192207
state.manifest = nextManifest;
193208
const toolAdapter = adapter();
194209
if (toolAdapter.ready) {
195-
const nextToolState = nextManifest.tools?.[toolId] || toolAdapter.createDefaultState(nextManifest.name);
196-
toolAdapter.applyState(cloneValue(nextToolState));
210+
const nextToolState = nextManifest.tools?.[toolId]
211+
? normalizeToolStateForProjectManifest(toolId, nextManifest.tools[toolId])
212+
: normalizeToolStateForProjectManifest(toolId, toolAdapter.createDefaultState(nextManifest.name));
213+
toolAdapter.applyState(cloneValue(unwrapToolStateForAdapter(toolId, nextToolState)));
197214
nextManifest.tools[toolId] = cloneValue(nextToolState);
215+
nextManifest.toolIntegration = buildProjectToolIntegration(nextManifest.tools);
198216
}
199217
state.appliedInitialState = true;
200218
markSaved("open-project");
@@ -224,8 +242,10 @@ export function createProjectSystemController(options = {}) {
224242
toolId
225243
});
226244
if (toolAdapter.ready) {
227-
nextManifest.tools[toolId] = toolAdapter.createDefaultState(fallbackName);
228-
toolAdapter.applyState(cloneValue(nextManifest.tools[toolId]));
245+
const defaultState = normalizeToolStateForProjectManifest(toolId, toolAdapter.createDefaultState(fallbackName));
246+
nextManifest.tools[toolId] = defaultState;
247+
nextManifest.toolIntegration = buildProjectToolIntegration(nextManifest.tools);
248+
toolAdapter.applyState(cloneValue(unwrapToolStateForAdapter(toolId, defaultState)));
229249
}
230250
state.manifest = nextManifest;
231251
state.appliedInitialState = true;

0 commit comments

Comments
 (0)