Skip to content

Commit 1524ae6

Browse files
author
DavidQ
committed
Remove legacy vectorMaps from Object Vector Studio V2 workspace launch payloads - PR_26133_123-object-vector-workspace-schema-cleanup
1 parent 1732b95 commit 1524ae6

6 files changed

Lines changed: 294 additions & 29 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# PR_26133_123 Object Vector Workspace Schema Cleanup Report
2+
3+
## Scope
4+
- Fixed Workspace Manager V2 Object Vector Studio V2 payload synthesis so workspace contexts, tool session hydration, saved context persistence, imports, saves, and launches only carry `version`, `toolId`, `name`, and `objects`.
5+
- Preserved Object Vector Studio V2 `objects[].tags` and `objects[].shapes[]` as the active geometry SSoT.
6+
- Added an actionable Object Vector Studio V2 import/schema failure for legacy root `vectorMaps`.
7+
- Updated targeted Workspace Manager V2/Object Vector validation to assert canonical payload keys and no workspace-launched `vectorMaps`.
8+
9+
## Files Changed
10+
- `tools/workspace-manager-v2/js/services/WorkspaceManagerV2ContextService.js`
11+
- `tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js`
12+
- `tests/tools/WorkspaceManagerV2ObjectVectorPayloadCleanup.test.mjs`
13+
- `tests/tools/ObjectVectorStudioV2DeleteCleanup.test.mjs`
14+
- `tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
15+
16+
## Validation
17+
- PASS: `node -e "import('./tests/tools/WorkspaceManagerV2ObjectVectorPayloadCleanup.test.mjs').then((m)=>m.run()).then(()=>console.log('PASS WorkspaceManagerV2ObjectVectorPayloadCleanup'))"`
18+
- PASS: `node -e "import('./tests/tools/ObjectVectorStudioV2DeleteCleanup.test.mjs').then((m)=>m.run()).then(()=>console.log('PASS ObjectVectorStudioV2DeleteCleanup'))"`
19+
- PASS: `node -e "import('./tests/games/AsteroidsValidation.test.mjs').then((m)=>m.run()).then(()=>console.log('PASS AsteroidsValidation'))"`
20+
- PASS: `node -e "import('./tests/games/AsteroidsAssetReferenceAdoption.test.mjs').then((m)=>m.run()).then(()=>console.log('PASS AsteroidsAssetReferenceAdoption'))"`
21+
- PASS: `node -e "import('./tests/games/AsteroidsPlatformDemo.test.mjs').then((m)=>m.run()).then(()=>console.log('PASS AsteroidsPlatformDemo'))"`
22+
- PASS: `node -e "const fs=require('fs'); ['tools/schemas/workspace.manifest.schema.json','tools/schemas/game.manifest.schema.json','tools/schemas/tools/object-vector-studio-v2.schema.json','games/Asteroids/game.manifest.json'].forEach((p)=>JSON.parse(fs.readFileSync(p,'utf8'))); console.log('PASS JSON parse schemas and Asteroids manifest');"`
23+
- PASS: `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list -g "shows Object Vector Studio V2 layout shell and schema-only palette gate|uses header lifecycle controls and launches tools from fixed Workspace Manager V2 tiles"`
24+
- PASS: `git diff --check`
25+
26+
## Coverage
27+
- PASS: Playwright V8 coverage report generated at `docs/dev/reports/playwright_v8_coverage_report.txt`.
28+
- PASS: Changed runtime JS guardrail generated at `docs/dev/reports/coverage_changed_js_guardrail.txt`.
29+
- Coverage guardrail: `tools/workspace-manager-v2/js/services/WorkspaceManagerV2ContextService.js` 83%, `tools/object-vector-studio-v2/js/services/ObjectVectorStudioV2SchemaService.js` 93%, no changed runtime JS coverage warnings.
30+
31+
## Full Samples Smoke Test
32+
- Skipped by request. This PR is limited to Object Vector Studio V2 schema/workspace launch cleanup and targeted Asteroids manifest validation.
33+
34+
## Manual Validation
35+
1. Open Workspace Manager V2.
36+
2. Pick the repo folder and select Asteroids.
37+
3. Confirm the generated Workspace JSON has `tools.object-vector-studio-v2` with only `version`, `toolId`, `name`, and `objects`.
38+
4. Launch Object Vector Studio V2 and confirm the status log reports 7 loaded objects with no `vectorMaps` in JSON details.
39+
5. Import an Object Vector Studio V2 JSON containing root `vectorMaps` and confirm the failure explains to remove `root.vectorMaps` and use `root.objects[].tags` plus `root.objects[].shapes`.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1942,6 +1942,20 @@ test.describe("Workspace Manager V2 bootstrap", () => {
19421942
await page.locator("#objectVectorStudioV2ImportJsonInput").setInputFiles(paletteDriftPayloadPath);
19431943
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Object Vector Studio V2 schema validation failed from import:object-vector-palette-drift\.json: root\.palette is not allowed\./);
19441944

1945+
const legacyVectorMapsPayloadPath = testInfo.outputPath("object-vector-legacy-vector-maps.json");
1946+
await writeFile(legacyVectorMapsPayloadPath, JSON.stringify({
1947+
name: "Legacy Vector Maps",
1948+
objects: [],
1949+
toolId: "object-vector-studio-v2",
1950+
vectorMaps: {
1951+
name: "Legacy Vector Maps",
1952+
shapes: []
1953+
},
1954+
version: 1
1955+
}, null, 2), "utf8");
1956+
await page.locator("#objectVectorStudioV2ImportJsonInput").setInputFiles(legacyVectorMapsPayloadPath);
1957+
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Object Vector Studio V2 schema validation failed from import:object-vector-legacy-vector-maps\.json: root\.vectorMaps is deprecated legacy vector-map data\. Remove root\.vectorMaps and import Object Vector Studio V2 payloads with root\.objects\[\]\.tags and root\.objects\[\]\.shapes only\./);
1958+
19451959
const unknownRootPayloadPath = testInfo.outputPath("object-vector-unknown-root.json");
19461960
await writeFile(unknownRootPayloadPath, JSON.stringify({
19471961
name: "Unknown Root",
@@ -9987,12 +10001,17 @@ test.describe("Workspace Manager V2 bootstrap", () => {
998710001
await expect(page.locator("#workspaceContextOutput")).toHaveValue(/"palette-manager-v2"/);
998810002
await expect(page.locator("#workspaceContextOutput")).toHaveValue(/"object-vector-studio-v2"/);
998910003
await expect(page.locator("#workspaceContextOutput")).not.toHaveValue(/"vector-map-editor"/);
9990-
await expect(page.locator("#workspaceContextOutput")).toHaveValue(/"vector\.asteroids\.ship"/);
10004+
await expect(page.locator("#workspaceContextOutput")).toHaveValue(/"object\.asteroids\.ship"/);
999110005
const activeWorkspaceOutput = JSON.parse(await page.locator("#workspaceContextOutput").inputValue());
10006+
const activeObjectVectorPayload = activeWorkspaceOutput.tools["object-vector-studio-v2"];
10007+
expect(Object.keys(activeObjectVectorPayload).sort()).toEqual(["name", "objects", "toolId", "version"]);
10008+
expect(activeObjectVectorPayload.vectorMaps).toBeUndefined();
10009+
expect(activeObjectVectorPayload.objects.every((object) => Array.isArray(object.tags) && Array.isArray(object.shapes))).toBe(true);
999210010
if (Object.hasOwn(activeWorkspaceOutput.tools, "text2speech-V2")) {
999310011
expect(activeWorkspaceOutput.tools["text2speech-V2"]).toEqual(expect.any(Array));
999410012
}
9995-
await expect(page.locator("#workspaceContextOutput")).toHaveValue(new RegExp('"vector' + "\\.asteroids"));
10013+
await expect(page.locator("#workspaceContextOutput")).not.toHaveValue(/"vectorMaps"/);
10014+
await expect(page.locator("#workspaceContextOutput")).not.toHaveValue(new RegExp('"vector' + "\\.asteroids"));
999610015
await expect(page.locator("#workspaceContextOutput")).not.toHaveValue(/"palette-browser"/);
999710016
await expect(page.locator("#workspaceContextOutput")).not.toHaveValue(/"asset-browser"/);
999810017
await expect(page.locator("#workspaceContextOutput")).not.toHaveValue(/"activePalette"/);
@@ -10079,23 +10098,25 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1007910098
expect(JSON.stringify(selectedGameHydration.toolSessions)).not.toMatch(/getDirectoryHandle|createWritable|FileSystemDirectoryHandle/);
1008010099
expect(Object.keys(selectedGameHydration.dataByTool["asset-manager-v2"].assets)).toHaveLength(15);
1008110100
expect(selectedGameHydration.dataByTool["object-vector-studio-v2"].objects.map((object) => object.id)).toEqual(expect.arrayContaining([
10101+
"object.asteroids.bullet",
1008210102
"object.asteroids.ship",
1008310103
"object.asteroids.large-asteroid",
1008410104
"object.asteroids.medium-asteroid",
10085-
"object.asteroids.medium-asteroid-2",
1008610105
"object.asteroids.small-asteroid",
1008710106
"object.asteroids.large-ufo",
1008810107
"object.asteroids.small-ufo"
1008910108
]));
1009010109
expect(selectedGameHydration.dataByTool["object-vector-studio-v2"].objects.map((object) => object.id)).toEqual(expect.arrayContaining([
10110+
"object.asteroids.bullet",
1009110111
"object.asteroids.ship",
1009210112
"object.asteroids.large-asteroid",
1009310113
"object.asteroids.medium-asteroid",
10094-
"object.asteroids.medium-asteroid-2",
1009510114
"object.asteroids.small-asteroid",
1009610115
"object.asteroids.large-ufo",
1009710116
"object.asteroids.small-ufo"
1009810117
]));
10118+
expect(Object.keys(selectedGameHydration.dataByTool["object-vector-studio-v2"]).sort()).toEqual(["name", "objects", "toolId", "version"]);
10119+
expect(selectedGameHydration.dataByTool["object-vector-studio-v2"].vectorMaps).toBeUndefined();
1009910120
expect(selectedGameHydration.dataByTool["object-vector-studio-v2"].assetLibrary).toBeUndefined();
1010010121
expect(selectedGameHydration.dataByTool["object-vector-studio-v2"].objects.every((object) => object.id.startsWith("object.") && !Object.hasOwn(object, "objectId"))).toBe(true);
1010110122
expect(selectedGameHydration.dataByTool["object-vector-studio-v2"].objects.every((object) => /^object\.[a-z0-9-]+\.[a-z0-9][a-z0-9-]*$/.test(object.id))).toBe(true);
@@ -10243,6 +10264,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1024310264
expect(asteroidsManifest.tools["palette-manager-v2"].swatches.length).toBeGreaterThan(0);
1024410265
expect(asteroidsManifest.tools["object-vector-studio-v2"].palette).toBeUndefined();
1024510266
expect(asteroidsManifest.tools["object-vector-studio-v2"].objects.map((object) => object.id)).toEqual(expect.arrayContaining([
10267+
"object.asteroids.bullet",
1024610268
"object.asteroids.ship",
1024710269
"object.asteroids.large-asteroid",
1024810270
"object.asteroids.medium-asteroid",
@@ -10251,10 +10273,10 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1025110273
"object.asteroids.small-ufo"
1025210274
]));
1025310275
expect(asteroidsManifest.tools["object-vector-studio-v2"].objects.map((object) => object.id)).toEqual(expect.arrayContaining([
10276+
"object.asteroids.bullet",
1025410277
"object.asteroids.ship",
1025510278
"object.asteroids.large-asteroid",
1025610279
"object.asteroids.medium-asteroid",
10257-
"object.asteroids.medium-asteroid-2",
1025810280
"object.asteroids.small-asteroid",
1025910281
"object.asteroids.large-ufo",
1026010282
"object.asteroids.small-ufo"
@@ -10364,6 +10386,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1036410386
expect(storedContext.repoPath).toBe(manifestRepoPath(server));
1036510387
expect(storedContext.tools["palette-manager-v2"].swatches.length).toBeGreaterThan(0);
1036610388
expect(storedContext.tools["object-vector-studio-v2"].palette).toBeUndefined();
10389+
expect(Object.keys(storedContext.tools["object-vector-studio-v2"]).sort()).toEqual(["name", "objects", "toolId", "version"]);
1036710390
expect(storedContext.tools["object-vector-studio-v2"].objects.map((object) => object.name)).toEqual(expect.arrayContaining([
1036810391
"Asteroids Ship",
1036910392
"Large Asteroid",
@@ -10477,10 +10500,10 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1047710500
objectVectorExtraKeys: [],
1047810501
objectVectorMissingKeys: [],
1047910502
objectVectorNames: expect.arrayContaining([
10503+
"Bullet",
1048010504
"Asteroids Ship",
1048110505
"Large Asteroid",
1048210506
"Medium Asteroid",
10483-
"Medium Asteroid 2",
1048410507
"Small Asteroid",
1048510508
"Large UFO",
1048610509
"Small UFO"
@@ -10519,19 +10542,20 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1051910542
await expect(page.locator("#objectVectorStudioV2PaletteSwatchCount")).toHaveText("(10 swatches)");
1052010543
await expect(page.locator("#objectVectorStudioV2PaletteSummary [data-palette-color]")).toHaveCount(10);
1052110544
await expect(page.locator("#statusLog")).toHaveValue(/INFO Runtime palette is read-only session\/workspace data; Object Vector JSON remains palette-free\./);
10522-
await expect(page.locator("#objectVectorStudioV2ObjectsCount")).toHaveText(/\(7 obj, states \d+, \d+ shapes\)/);
10545+
await expect(page.locator("#objectVectorStudioV2ObjectsCount")).toHaveText(/\(7 obj, states \d+, \d+ shapes?\)/);
1052310546
await expect(page.locator("#objectVectorStudioV2ObjectTiles")).toContainText("Asteroids Ship");
1052410547
await expect(page.locator("#objectVectorStudioV2ObjectTiles")).toContainText("Large Asteroid");
1052510548
await expect(page.locator("#objectVectorStudioV2ObjectTiles")).toContainText("Large UFO");
1052610549
const smallUfoTile = page.locator('.object-vector-studio-v2__object-tile[data-object-id="object.asteroids.small-ufo"]');
1052710550
await smallUfoTile.scrollIntoViewIfNeeded();
1052810551
await smallUfoTile.click();
1052910552
await expect(smallUfoTile).toContainText("object > asteroids > Small UFO");
10530-
await expect(page.locator(".object-vector-studio-v2__object-tile.is-selected .object-vector-studio-v2__object-tile-shapes [data-object-tile-shape-index] .object-vector-studio-v2__shape-select-label")).toHaveText(["1. Ellipse", "0. Ellipse"]);
10553+
await expect(page.locator(".object-vector-studio-v2__object-tile.is-selected .object-vector-studio-v2__object-tile-shapes [data-object-tile-shape-index] .object-vector-studio-v2__shape-select-label")).toHaveText(["0. Polyline"]);
1053110554
await page.locator('button[aria-controls="objectVectorStudioV2DependencyGraphContent"]').click();
1053210555
await expect(page.locator("#objectVectorStudioV2DependencyGraph")).toContainText("Object tags:");
1053310556
await expect(page.locator("#objectVectorStudioV2DependencyGraph")).toContainText("object.asteroids.ship: Asteroids Ship");
1053410557
await expect(page.locator("#objectVectorStudioV2JsonDetails")).not.toContainText('"palette"');
10558+
await expect(page.locator("#objectVectorStudioV2JsonDetails")).not.toContainText('"vectorMaps"');
1053510559
await expect(page.locator("#statusLog")).toHaveValue(/OK Runtime palette loaded from workspace\.tools\.palette-manager-v2\.data: 10 swatches\./);
1053610560
await expect(page.locator("#statusLog")).toHaveValue(/OK Loaded Object Vector Studio V2 schema payload from workspace\.tools\.object-vector-studio-v2: 7 objects\./);
1053710561
await page.locator("#returnToWorkspaceButton").click();
@@ -11102,7 +11126,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1110211126

1110311127
await page.locator('[data-workspace-tool-id="object-vector-studio-v2"]').click();
1110411128
await expect(page).toHaveURL(/object-vector-studio-v2\/index\.html.*launch=workspace/);
11105-
await expect(page.locator("#objectVectorStudioV2ObjectsCount")).toHaveText(/\(7 obj, states \d+, \d+ shapes\)/);
11129+
await expect(page.locator("#objectVectorStudioV2ObjectsCount")).toHaveText(/\(7 obj, states \d+, \d+ shapes?\)/);
1110611130
await expect(page.locator("#statusLog")).toHaveValue(/OK Loaded Object Vector Studio V2 schema payload from workspace\.tools\.object-vector-studio-v2: 7 objects\./);
1110711131

1110811132
let objectVectorSession = await readObjectVectorWorkspaceSession(page);

tests/tools/ObjectVectorStudioV2DeleteCleanup.test.mjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ export async function run() {
8080
const legacyVectorValidation = service.validatePayload(legacyVectorPayload);
8181
assert.equal(legacyVectorValidation.ok, false);
8282
assert.equal(
83-
legacyVectorValidation.errors.some((message) => message === "root.vectorMaps is not allowed."),
83+
legacyVectorValidation.errors.some((message) => message.includes("root.vectorMaps is deprecated legacy vector-map data")),
84+
true,
85+
);
86+
assert.equal(
87+
legacyVectorValidation.errors.some((message) => message.includes("root.objects[].tags and root.objects[].shapes only")),
8488
true,
8589
);
8690

0 commit comments

Comments
 (0)