Skip to content

Commit e56a8e0

Browse files
author
DavidQ
committed
Use workspace manifest schema as Workspace Manager V2 SSoT - PR_26126_115-workspace-manager-v2-manifest-schema-ssot
1 parent 7605623 commit e56a8e0

12 files changed

Lines changed: 299 additions & 119 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# PR_26126_115 Generated Manifest Validation Notes
2+
3+
## Validation
4+
- A direct Node validation instantiated `WorkspaceManagerV2ContextService`.
5+
- It loaded `games/Asteroids/game.manifest.json`.
6+
- It loaded `tools/schemas/workspace.manifest.schema.json`.
7+
- It generated the Workspace Manager V2 manifest and validated it before launch/use.
8+
9+
## Result
10+
- Generated Workspace Manager V2 manifest validation: PASS.
11+
12+
## Validated Shape
13+
- `documentKind`: `workspace-manifest`
14+
- `gameId`: `Asteroids`
15+
- `gameRoot`: `games/Asteroids/`
16+
- `assetsPath`: `games/Asteroids/assets`
17+
- `tools`: `asset-manager-v2`, `palette-browser`
18+
- `tools.asset-manager-v2` keys: `assets`
19+
- `tools.palette-browser.swatches`: 11
20+
21+
## Rejected Shape
22+
- Old wrapper context JSON containing `toolId`, `activePalette`, or nested `workspaceManifest` is rejected before Asset Manager V2 launch.
23+
- Old `tools.asset-browser` generated manifest payloads are rejected in Workspace Manager V2 production launch mode.
24+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# PR_26126_115 Manual Validation Notes
2+
3+
## Automated Validation
4+
- `npm run test:workspace-v2` passed.
5+
- Result: 19 Playwright tests passed.
6+
- Direct generated manifest validation passed against `tools/schemas/workspace.manifest.schema.json`.
7+
8+
## Manual Checks
9+
1. Open `tools/workspace-manager-v2/index.html`.
10+
- Expected: selecting Asteroids produces a manifest-only JSON object in Workspace Context.
11+
- Expected: the JSON includes `documentKind`, `schema`, `version`, `id`, `name`, `gameId`, `gameRoot`, `assetsPath`, and `tools`.
12+
- Expected: the JSON does not include root `toolId`, root `activePalette`, or nested `workspaceManifest`.
13+
2. Inspect generated `tools`.
14+
- Expected: `tools.asset-manager-v2.assets` exists.
15+
- Expected: `tools.palette-browser.swatches` exists.
16+
- Expected: `tools.asset-browser` is not generated.
17+
3. Launch Asset Manager V2 from Workspace Manager V2.
18+
- Expected: Asset Manager V2 loads the schema-valid manifest from sessionStorage and remains usable.
19+
- Expected: validated assets insert into `tools.asset-manager-v2.assets`.
20+
4. Open `tools/asset-manager-v2/index.html`.
21+
- Expected: direct launch without valid manifest context hard-fails to the launch guard overlay.
22+
23+
## Out Of Scope
24+
- Full samples smoke test was skipped because this PR is scoped to Workspace Manager V2 manifest handoff and targeted Playwright coverage.
25+
- Deprecated `tools/workspace-v2/` was not modified.
26+
- Sample JSON was not modified.
27+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# PR_26126_115 Workspace Manifest SSoT Notes
2+
3+
## Manifest Schema
4+
- `tools/schemas/workspace.manifest.schema.json` is now the source of truth for Workspace Manager V2 generated launch data.
5+
- The schema now requires active game context directly on the workspace manifest:
6+
- `gameId`
7+
- `gameRoot`
8+
- `assetsPath`
9+
- The schema now requires the Asset Manager V2 payload at `tools.asset-manager-v2`.
10+
- `tools.palette-browser` remains the required palette key because the current schema still defines the palette contract there.
11+
12+
## Generated Manifest
13+
- Workspace Manager V2 now generates and stores only the workspace manifest JSON.
14+
- The old wrapper shape is no longer generated:
15+
- no root `toolId`
16+
- no root `activePalette`
17+
- no nested `workspaceManifest`
18+
- Generated manifests use `tools.asset-manager-v2.assets`.
19+
- Generated manifests do not include `tools.asset-browser`.
20+
21+
## Launch Handoff
22+
- Workspace Manager V2 validates the generated manifest against `workspace.manifest.schema.json` before enabling launch/use.
23+
- Asset Manager V2 reads the `hostContextId` value as the schema-valid workspace manifest itself.
24+
- Asset Manager V2 rejects old wrapper context JSON and manifests that still use `tools.asset-browser`.
25+
- Asset Manager V2 writes validated assets back to `tools.asset-manager-v2.assets`.
26+
27+
## Scope
28+
- Deprecated `tools/workspace-v2/` was not modified.
29+
- Sample JSON was not modified.
30+
- Temporary `?workspace=UAT` remains isolated and unchanged.
31+

tests/playwright/tools/AssetManagerV2.spec.mjs

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,28 +1159,24 @@ test.describe("Asset Manager V2", () => {
11591159

11601160
test("shows Asset Manager V2 launch guard when Workspace Manager V2 palette context is missing", async ({ page }) => {
11611161
const server = await openAssetManagerWithSessionContext(page, {
1162-
version: "workspace-manager-v2",
1163-
toolId: "asset-manager-v2",
1162+
documentKind: "workspace-manifest",
1163+
schema: "html-js-gaming.project",
1164+
version: 1,
1165+
id: "workspace-manager-v2-Asteroids",
1166+
name: "Asteroids Workspace Manager V2 Context",
11641167
gameId: "Asteroids",
11651168
gameRoot: "games/Asteroids/",
11661169
assetsPath: "games/Asteroids/assets",
1167-
workspaceManifest: {
1168-
documentKind: "workspace-manifest",
1169-
schema: "html-js-gaming.project",
1170-
version: 1,
1171-
id: "workspace-manager-v2-Asteroids",
1172-
name: "Asteroids Workspace Manager V2 Context",
1173-
tools: {
1174-
"palette-browser": {
1175-
schema: "html-js-gaming.palette",
1176-
version: 1,
1177-
name: "Asteroids Palette",
1178-
source: "workspace-manager-v2",
1179-
swatches: []
1180-
},
1181-
"asset-browser": {
1182-
assets: {}
1183-
}
1170+
tools: {
1171+
"palette-browser": {
1172+
schema: "html-js-gaming.palette",
1173+
version: 1,
1174+
name: "Asteroids Palette",
1175+
source: "workspace-manager-v2",
1176+
swatches: []
1177+
},
1178+
"asset-manager-v2": {
1179+
assets: {}
11841180
}
11851181
}
11861182
});
@@ -1195,7 +1191,7 @@ test.describe("Asset Manager V2", () => {
11951191
await expect(page).toHaveURL(/fromTool=workspace-manager-v2/);
11961192
await expect(page.locator("#assetLaunchGuard")).toBeVisible();
11971193
await expect(page.locator("#assetLaunchGuardMessage")).toHaveText("Asset Manager V2 is only available through Workspace Manager with a game workspace and palette.");
1198-
await expect(page.locator("#assetLaunchGuardReason")).toContainText("Workspace Manager V2 session context is missing active palette swatches.");
1194+
await expect(page.locator("#assetLaunchGuardReason")).toContainText("Workspace Manager V2 manifest is missing active palette swatches.");
11991195
await expect(page.locator("body")).toHaveClass(/asset-manager-v2--launch-blocked/);
12001196

12011197
expect(pageErrors).toEqual([]);
@@ -1241,7 +1237,7 @@ test.describe("Asset Manager V2", () => {
12411237
try {
12421238
await expect(page.locator("#assetLaunchGuard")).toBeVisible();
12431239
await expect(page.locator("#assetLaunchGuardMessage")).toHaveText("Asset Manager V2 is only available through Workspace Manager with a game workspace and palette.");
1244-
await expect(page.locator("#assetLaunchGuardReason")).toContainText("Workspace Manager V2 session context is required.");
1240+
await expect(page.locator("#assetLaunchGuardReason")).toContainText("Workspace Manager V2 launch requires a schema-valid workspace manifest.");
12451241
await expect(page.locator("body")).toHaveClass(/asset-manager-v2--launch-blocked/);
12461242
expect(pageErrors).toEqual([]);
12471243
} finally {
@@ -1284,20 +1280,22 @@ test.describe("Asset Manager V2", () => {
12841280
await page.locator("#activeGameSelect").selectOption("Asteroids");
12851281
await expect(page.locator("#workspaceContextOutput")).toContainText('"gameRoot": "games/Asteroids/"');
12861282
await expect(page.locator("#workspaceContextOutput")).toContainText('"assetsPath": "games/Asteroids/assets"');
1287-
await expect(page.locator("#workspaceContextOutput")).toContainText('"activePalette"');
1283+
await expect(page.locator("#workspaceContextOutput")).toContainText('"asset-manager-v2"');
1284+
await expect(page.locator("#workspaceContextOutput")).not.toContainText('"activePalette"');
1285+
await expect(page.locator("#workspaceContextOutput")).not.toContainText('"workspaceManifest"');
12881286
await expect(page.locator("#launchAssetManagerV2Button")).toBeEnabled();
12891287
await page.locator("#launchAssetManagerV2Button").click();
12901288
await expect(page).toHaveURL(/asset-manager-v2\/index\.html.*launch=workspace/);
12911289
await expect(page).toHaveURL(/fromTool=workspace-manager-v2/);
12921290
await expect(page).not.toHaveURL(/gameId=Asteroids/);
12931291
await expect(page.locator(".asset-manager-v2__tool__menu")).toBeHidden();
12941292
await expect(page.locator(".asset-manager-v2__workspace__menu")).toBeVisible();
1295-
await expect(page.locator("#statusLog")).toHaveValue(/Workspace Manager V2 loaded 0 validated Asset Manager V2 assets/);
1293+
await expect(page.locator("#statusLog")).toHaveValue(/Workspace Manager V2 loaded 0 validated assets from tools\.asset-manager-v2\.assets/);
12961294
await expect(page.locator("#statusLog")).toHaveValue(/Workspace Manager V2 loaded \d+ palette colors from active palette context/);
12971295
const hostContextId = await page.evaluate(() => new URL(window.location.href).searchParams.get("hostContextId"));
12981296
const initialAssetCount = await page.evaluate((id) => {
12991297
const context = JSON.parse(sessionStorage.getItem(id));
1300-
return Object.keys(context.workspaceManifest.tools["asset-browser"].assets).length;
1298+
return Object.keys(context.tools["asset-manager-v2"].assets).length;
13011299
}, hostContextId);
13021300
expect(initialAssetCount).toBe(0);
13031301
const workspacePreviewContext = await page.evaluate(async () => {
@@ -1401,31 +1399,36 @@ test.describe("Asset Manager V2", () => {
14011399

14021400
await expect(page.locator("#workspaceInsertAssetsButton")).toBeEnabled();
14031401
await page.locator("#workspaceInsertAssetsButton").click();
1404-
await expect(page.locator("#statusLog")).toHaveValue(/OK Inserted 4 validated Asset Manager V2 assets into Workspace Manager V2 context/);
1402+
await expect(page.locator("#statusLog")).toHaveValue(/OK Inserted 4 validated assets into Workspace Manager V2 tools\.asset-manager-v2\.assets/);
14051403

14061404
const storedContext = await page.evaluate((id) => JSON.parse(sessionStorage.getItem(id)), hostContextId);
1407-
expect(storedContext.workspaceManifest.tools["asset-browser"].assets["assets.audio.sound.fire"]).toEqual({
1405+
expect(storedContext.documentKind).toBe("workspace-manifest");
1406+
expect(storedContext.toolId).toBeUndefined();
1407+
expect(storedContext.activePalette).toBeUndefined();
1408+
expect(storedContext.workspaceManifest).toBeUndefined();
1409+
expect(storedContext.tools["asset-browser"]).toBeUndefined();
1410+
expect(storedContext.tools["asset-manager-v2"].assets["assets.audio.sound.fire"]).toEqual({
14081411
path: "assets/audio/fire.wav",
14091412
type: "audio",
14101413
kind: "wav",
14111414
role: "sound",
14121415
source: "asset-manager-v2"
14131416
});
1414-
expect(storedContext.workspaceManifest.tools["asset-browser"].assets["assets.font.ui.vector-battle"]).toEqual({
1417+
expect(storedContext.tools["asset-manager-v2"].assets["assets.font.ui.vector-battle"]).toEqual({
14151418
path: "assets/fonts/vector_battle.ttf",
14161419
type: "font",
14171420
kind: "ttf",
14181421
role: "ui",
14191422
source: "asset-manager-v2"
14201423
});
1421-
expect(storedContext.workspaceManifest.tools["asset-browser"].assets["assets.image.sprite.preview"]).toEqual({
1424+
expect(storedContext.tools["asset-manager-v2"].assets["assets.image.sprite.preview"]).toEqual({
14221425
path: "assets/images/preview.png",
14231426
type: "image",
14241427
kind: "png",
14251428
role: "sprite",
14261429
source: "asset-manager-v2"
14271430
});
1428-
expect(storedContext.workspaceManifest.tools["asset-browser"].assets["assets.color.hud.primary-hud.hud-blue"]).toEqual({
1431+
expect(storedContext.tools["asset-manager-v2"].assets["assets.color.hud.primary-hud.hud-blue"]).toEqual({
14291432
path: "palette://workspace/hud-blue",
14301433
type: "color",
14311434
kind: "hex",
@@ -1437,11 +1440,10 @@ test.describe("Asset Manager V2", () => {
14371440
symbol: "*"
14381441
}
14391442
});
1440-
expect(storedContext.activePalette.source).toBe("workspace-manager-v2");
1441-
expect(storedContext.activePalette.swatches.length).toBeGreaterThan(0);
1442-
expect(storedContext.workspaceManifest.tools["asset-manager-v2"]).toBeUndefined();
1443-
expect(storedContext.workspaceManifest.tools["workspace-v2"]).toBeUndefined();
1444-
expect(Object.keys(storedContext.workspaceManifest.tools).sort()).toEqual(["asset-browser", "palette-browser"]);
1443+
expect(storedContext.tools["palette-browser"].source).toBe("workspace-manager-v2");
1444+
expect(storedContext.tools["palette-browser"].swatches.length).toBeGreaterThan(0);
1445+
expect(storedContext.tools["workspace-v2"]).toBeUndefined();
1446+
expect(Object.keys(storedContext.tools).sort()).toEqual(["asset-manager-v2", "palette-browser"]);
14451447

14461448
expect(pageErrors).toEqual([]);
14471449
} finally {

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,15 @@ test.describe("Workspace Manager V2 bootstrap", () => {
146146
await expect(page.locator("#activeGameSummary")).toContainText("games/Asteroids/");
147147
await expect(page.locator("#activePaletteSummary")).toContainText("Asteroids Palette");
148148
await expect(page.locator("#activePaletteSummary")).toContainText("active colors");
149-
await expect(page.locator("#activeAssetRegistrySummary")).toHaveText("Schema-ready Asset Manager V2 registry context contains 0 managed assets.");
150-
await expect(page.locator("#launchContextSummary")).toHaveText("Session launch context is ready for Asteroids.");
149+
await expect(page.locator("#activeAssetRegistrySummary")).toHaveText("Schema-ready Asset Manager V2 manifest payload contains 0 managed assets.");
150+
await expect(page.locator("#launchContextSummary")).toHaveText("Schema-valid manifest is ready for Asteroids.");
151151
await expect(page.locator("#workspaceContextOutput")).toContainText('"gameRoot": "games/Asteroids/"');
152152
await expect(page.locator("#workspaceContextOutput")).toContainText('"assetsPath": "games/Asteroids/assets"');
153-
await expect(page.locator("#workspaceContextOutput")).toContainText('"activePalette"');
154153
await expect(page.locator("#workspaceContextOutput")).toContainText('"source": "workspace-manager-v2"');
154+
await expect(page.locator("#workspaceContextOutput")).toContainText('"asset-manager-v2"');
155+
await expect(page.locator("#workspaceContextOutput")).not.toContainText('"activePalette"');
156+
await expect(page.locator("#workspaceContextOutput")).not.toContainText('"toolId"');
157+
await expect(page.locator("#workspaceContextOutput")).not.toContainText('"workspaceManifest"');
155158
await expect(page.locator("#workspaceContextOutput")).not.toContainText('"workspaceMetadata"');
156159
await expect(page.locator("#workspaceContextOutput")).not.toContainText("samples/");
157160
await expect(page.locator("#workspaceContextOutput")).not.toContainText("tools/");
@@ -167,7 +170,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
167170
await expect(page.locator("#assetLaunchGuard")).toBeHidden();
168171
await expect(page.locator(".asset-manager-v2__tool__menu")).toBeHidden();
169172
await expect(page.locator(".asset-manager-v2__workspace__menu")).toBeVisible();
170-
await expect(page.locator("#statusLog")).toHaveValue(/Workspace Manager V2 loaded 0 validated Asset Manager V2 assets/);
173+
await expect(page.locator("#statusLog")).toHaveValue(/Workspace Manager V2 loaded 0 validated assets from tools\.asset-manager-v2\.assets/);
171174
await expect(page.locator("#statusLog")).toHaveValue(/Workspace Manager V2 loaded \d+ palette colors from active palette context/);
172175

173176
const workspacePreviewContext = await page.evaluate(async () => {
@@ -187,27 +190,28 @@ test.describe("Workspace Manager V2 bootstrap", () => {
187190
const hostContextId = url.searchParams.get("hostContextId");
188191
return JSON.parse(sessionStorage.getItem(hostContextId));
189192
});
190-
expect(storedContext.version).toBe("workspace-manager-v2");
193+
expect(storedContext.documentKind).toBe("workspace-manifest");
194+
expect(storedContext.toolId).toBeUndefined();
195+
expect(storedContext.activePalette).toBeUndefined();
196+
expect(storedContext.workspaceManifest).toBeUndefined();
197+
expect(storedContext.gameId).toBe("Asteroids");
191198
expect(storedContext.gameRoot).toBe("games/Asteroids/");
192199
expect(storedContext.assetsPath).toBe("games/Asteroids/assets");
193-
expect(storedContext.activePalette.source).toBe("workspace-manager-v2");
194-
expect(storedContext.activePalette.swatches.length).toBeGreaterThan(0);
195-
expect(storedContext.workspaceManifest.tools["palette-browser"].swatches.length).toBeGreaterThan(0);
196-
expect(storedContext.workspaceManifest.tools["asset-browser"].assets).toEqual({});
197-
expect(storedContext.workspaceManifest.gameId).toBeUndefined();
198-
expect(storedContext.workspaceManifest.workspaceMetadata).toBeUndefined();
199-
expect(storedContext.workspaceManifest.tools["asset-browser"]).toEqual({ assets: {} });
200+
expect(storedContext.tools["palette-browser"].swatches.length).toBeGreaterThan(0);
201+
expect(storedContext.tools["asset-manager-v2"].assets).toEqual({});
202+
expect(storedContext.workspaceMetadata).toBeUndefined();
203+
expect(storedContext.tools["asset-browser"]).toBeUndefined();
204+
expect(storedContext.tools["asset-manager-v2"]).toEqual({ assets: {} });
200205
const schemaValidation = await page.evaluate(async () => {
201206
const [workspaceSchema, paletteSchema, assetSchema] = await Promise.all([
202207
fetch("/tools/schemas/workspace.manifest.schema.json", { cache: "no-store" }).then((response) => response.json()),
203208
fetch("/tools/schemas/tools/palette-browser.schema.json", { cache: "no-store" }).then((response) => response.json()),
204209
fetch("/tools/schemas/tools/asset-browser.schema.json", { cache: "no-store" }).then((response) => response.json())
205210
]);
206211
const url = new URL(window.location.href);
207-
const context = JSON.parse(sessionStorage.getItem(url.searchParams.get("hostContextId")));
208-
const manifest = context.workspaceManifest;
212+
const manifest = JSON.parse(sessionStorage.getItem(url.searchParams.get("hostContextId")));
209213
const palettePayload = manifest.tools["palette-browser"];
210-
const assetPayload = manifest.tools["asset-browser"];
214+
const assetPayload = manifest.tools["asset-manager-v2"];
211215
const extraKeys = (value, schema) => Object.keys(value).filter((key) => !Object.hasOwn(schema.properties || {}, key));
212216
const missingKeys = (value, schema) => (schema.required || []).filter((key) => !Object.hasOwn(value, key));
213217
const swatchExtraKeys = palettePayload.swatches.flatMap((swatch, index) => (
@@ -238,7 +242,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
238242
paletteMissingKeys: [],
239243
swatchExtraKeys: [],
240244
swatchMissingKeys: [],
241-
toolKeys: ["asset-browser", "palette-browser"],
245+
toolKeys: ["asset-manager-v2", "palette-browser"],
242246
unsupportedToolKeys: []
243247
});
244248
expect(JSON.stringify(storedContext)).not.toMatch(/samples\//i);

0 commit comments

Comments
 (0)