Skip to content

Commit 92b612d

Browse files
author
DavidQ
committed
Enforce object ID as runtime SSoT and clean up triangle and frame controls - PR_26133_026-object-id-ssot-schema-and-editor-controls
1 parent 3081767 commit 92b612d

10 files changed

Lines changed: 184 additions & 67 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Object ID SSoT Migration Report
2+
3+
Task: PR_26133_026-object-id-ssot-schema-and-editor-controls
4+
Date: 2026-05-13
5+
6+
## Runtime Identity Contract
7+
8+
Object IDs are now the single source of truth for Asteroids Object Vector runtime identity. Runtime game selection uses `object.asteroids.*` IDs directly instead of resolving through `asset.asteroids.*` IDs or `vector.asteroids.*` editor IDs.
9+
10+
## Removed Or Replaced Runtime Identity
11+
12+
- Replaced Asteroids scene constants that selected `asset.asteroids.*` with `object.asteroids.*` constants.
13+
- Replaced Asteroids scene render calls from `assetId` to `objectId` for ship, asteroid sizes, and UFO sizes.
14+
- Replaced Asteroids platform demo runtime visual preference `vectorIds` with runtime `objectIds`.
15+
16+
## What Remains And Why
17+
18+
- `asset.asteroids.*` remains in `workspace.tools.object-vector-studio-v2.assetLibrary` because those entries are editor/library assets that map to Object Vector objects. Runtime scene selection no longer depends on them.
19+
- `vector.asteroids.*` remains in `workspace.tools.vector-map-editor.vectorMapDocument.vectors` and vector document asset refs because those are editor/vector-document geometry IDs, not game runtime object identities.
20+
- Object Vector object records remain under `workspace.tools.object-vector-studio-v2.objects`; the runtime loader resolves these objects by object ID.
21+
22+
## Schema Changes
23+
24+
- Added `game.gameData.objectVectorRuntime.objectIds` to `tools/schemas/game.manifest.schema.json`.
25+
- The schema constrains runtime object references with the `^object\.` identity pattern so asset IDs and vector IDs are rejected when this contract is used.
26+
- Updated `games/Asteroids/game.manifest.json` to declare ship, asteroid, and UFO runtime object IDs in `game.gameData.objectVectorRuntime.objectIds`.
27+
28+
## Editor Controls
29+
30+
- Triangle-labeled Object Vector polygon shapes hide Add Point, Delete Point, and point selection controls.
31+
- Editable non-triangle polygons retain Add Point and Delete Point behavior.
32+
- Duplicate Frame now renders between Frame Earlier and Frame Later.
33+
34+
## Validation
35+
36+
- PASS - Asteroids game manifest validates against the updated schema.
37+
- PASS - Asteroids small asteroid resolves by `objectId: object.asteroids.asteroid.small`.
38+
- PASS - Object Vector Studio V2 loads Asteroids objects.
39+
- PASS - Triangle/non-triangle polygon controls verified in Playwright.
40+
- PASS - Duplicate Frame ordering verified in Playwright.
41+
- PASS - `npm run test:workspace-v2` completed with 49 passed.

docs/dev/reports/playwright_v8_coverage_report.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# PR_26133_025 Playwright V8 Coverage Report
1+
# PR_26133_026 Playwright V8 Coverage Report
22

3-
Task: PR_26133_025-object-vector-studio-dirty-state-save-tracking
3+
Task: PR_26133_026-object-id-ssot-schema-and-editor-controls
44
Date: 2026-05-13
55

66
## Result
@@ -23,17 +23,18 @@ PASS - Coverage reporting was generated during `npm run test:workspace-v2`.
2323

2424
## Changed Runtime JS Coverage
2525

26-
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: 94% function coverage, 4171/4171 reported lines executed, 450/481 reported functions executed.
26+
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: 94% function coverage, 4190/4190 reported lines executed, 452/483 reported functions executed.
27+
- `games/Asteroids/game/AsteroidsGameScene.js`: 52% function coverage, 846/846 reported lines executed, 25/48 reported functions executed.
28+
- `tools/shared/asteroidsPlatformDemo.js`: not collected by Playwright V8 coverage; covered by focused node test `AsteroidsPlatformDemo.test.mjs`.
2729

2830
## PR-Specific Coverage/Validation Relevance
2931

30-
The new Workspace Manager V2 Playwright coverage exercises the Object Vector Studio V2 workspace dirty contract end to end:
32+
The workspace-v2 run exercises the Object Vector Studio V2 editor and Asteroids runtime paths changed in this PR:
3133

32-
- clean startup session state,
33-
- selection/preview actions remaining clean,
34-
- every requested persisted edit category marking the Object Vector session dirty,
35-
- Workspace Manager Save becoming enabled after return,
36-
- invalid save preserving dirty state without manifest writes,
37-
- successful save clearing dirty state only after verified write-back.
34+
- triangle geometry layout and controls,
35+
- frame control ordering,
36+
- objectId-based Asteroids Object Vector runtime rendering,
37+
- Workspace Manager game manifest schema loading and validation,
38+
- Object Vector Studio V2 loading of Asteroids workspace objects.
3839

3940
Generated source report: `docs/dev/reports/playwright_v8_coverage_report.txt`.
Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
1-
# PR_26133_025 Workspace V2 Validation
1+
# PR_26133_026 Workspace V2 Validation
22

3-
Task: PR_26133_025-object-vector-studio-dirty-state-save-tracking
3+
Task: PR_26133_026-object-id-ssot-schema-and-editor-controls
44
Date: 2026-05-13
55

66
## Result
77

88
PASS - `npm run test:workspace-v2`
99

1010
- 49 Playwright tests passed.
11-
- Focused Object Vector Studio V2 dirty-state test passed before the full run.
12-
- No console/runtime page errors were reported by the new dirty-state coverage.
11+
- Focused Object Vector Studio V2 checks passed before the full run.
12+
- Asteroids runtime Object Vector rendering passed with objectId-based lookup.
1313
- No sample JSON files were changed.
1414

15-
## Targeted Object Vector Studio V2 Verification
16-
17-
- PASS - Object Vector Studio V2 workspace launches start with `workspace.tools.object-vector-studio-v2.dirty.isDirty=false`.
18-
- PASS - Selection-only changes do not mark the workspace tool session dirty.
19-
- PASS - Preview-only actions tested through zoom, pan, and grid visibility do not change persisted Object Vector data or dirty state.
20-
- PASS - Persisted object edits mark `workspace.tools.object-vector-studio-v2` dirty.
21-
- PASS - Persisted object geometry edits mark dirty.
22-
- PASS - Persisted object transform edits mark dirty.
23-
- PASS - Persisted palette/color application marks dirty.
24-
- PASS - Shape add, visibility, lock/unlock, and delete edits mark dirty.
25-
- PASS - Object add, rename, duplicate, and delete edits mark dirty.
26-
- PASS - Returning to Workspace Manager V2 enables Save and marks the Object Vector Studio V2 tile dirty.
27-
- PASS - Failed invalid save keeps dirty state active and does not write a manifest.
28-
- PASS - Successful save clears dirty state only after verified manifest write-back.
15+
## Targeted Verification
16+
17+
- PASS - Triangle Geometry hides Add Point and Delete Point controls.
18+
- PASS - Non-triangle polygon geometry keeps Add Point and Delete Point controls.
19+
- PASS - Frame controls render as Frame Earlier, Duplicate Frame, Frame Later.
20+
- PASS - Asteroids runtime renders ship, asteroid, and UFO Object Vector objects using object.asteroids.* IDs.
21+
- PASS - Asteroids small asteroid resolves through objectId object.asteroids.asteroid.small.
22+
- PASS - Object Vector Studio V2 still loads Asteroids objects from workspace.tools.object-vector-studio-v2.
23+
- PASS - game.gameData.objectVectorRuntime.objectIds contains only object.* runtime references.
24+
- PASS - No runtime console/page errors were reported by the targeted Playwright coverage.
2925

3026
## Commands
3127

3228
- PASS - `node --check tools/object-vector-studio-v2/js/ToolStarterApp.js`
29+
- PASS - `node --check games/Asteroids/game/AsteroidsGameScene.js`
30+
- PASS - `node --check tools/shared/asteroidsPlatformDemo.js`
3331
- PASS - `node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
34-
- PASS - `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list -g "tracks Object Vector Studio V2 dirty state"`
32+
- PASS - `node -e "JSON.parse(...)"` for schema and Asteroids manifest.
33+
- PASS - WorkspaceManagerV2ContextService game manifest validation script.
34+
- PASS - ObjectVectorRuntimeAssetService objectId lookup script for object.asteroids.asteroid.small.
35+
- PASS - `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list -g "compacts Object Vector Studio V2 geometry layouts|supports Object Vector Studio V2 animation states|loads Object Vector Studio V2 runtime assets"`
3536
- PASS - `npm run test:workspace-v2`
36-
- PASS - `git diff --check HEAD` completed with line-ending warnings only.
3737

3838
## Notes
3939

40-
The full workspace-v2 run produced transient Asteroids manifest write-back output from existing save tests. That generated file noise was restored after validation; the final tracked diff contains only Object Vector Studio V2 dirty tracking and its Playwright test coverage.
40+
Asset IDs remain in Object Vector Studio V2 assetLibrary as editor/library asset entries. Runtime game selection now uses object IDs directly.

games/Asteroids/game.manifest.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
"directPath": "/games/Asteroids/index.html",
1212
"workspaceManagerPath": "/tools/workspace-manager-v2/index.html?gameId=Asteroids",
1313
"workspaceManagerOptional": true
14+
},
15+
"objectVectorRuntime": {
16+
"objectIds": {
17+
"ship": "object.asteroids.ship",
18+
"asteroidLarge": "object.asteroids.asteroid.large",
19+
"asteroidMedium": "object.asteroids.asteroid.medium",
20+
"asteroidSmall": "object.asteroids.asteroid.small",
21+
"ufoLarge": "object.asteroids.ufo.large",
22+
"ufoSmall": "object.asteroids.ufo.small"
23+
}
1424
}
1525
},
1626
"workspace": {

games/Asteroids/game/AsteroidsGameScene.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ const SCORE_TWO_X = 824;
2525
const LIFE_SPACING = 22;
2626
const PAUSE_OVERLAY_COLOR = 'rgba(2, 6, 23, 0.58)';
2727
const INITIALS_OVERLAY_COLOR = 'rgba(1, 6, 19, 0.62)';
28-
const ASTEROID_OBJECT_VECTOR_ASSET_IDS = Object.freeze({
29-
1: "asset.asteroids.asteroid.small",
30-
2: "asset.asteroids.asteroid.medium",
31-
3: "asset.asteroids.asteroid.large",
28+
const ASTEROID_OBJECT_VECTOR_OBJECT_IDS = Object.freeze({
29+
1: "object.asteroids.asteroid.small",
30+
2: "object.asteroids.asteroid.medium",
31+
3: "object.asteroids.asteroid.large",
3232
});
33-
const UFO_OBJECT_VECTOR_ASSET_IDS = Object.freeze({
34-
large: "asset.asteroids.ufo.large",
35-
small: "asset.asteroids.ufo.small",
33+
const UFO_OBJECT_VECTOR_OBJECT_IDS = Object.freeze({
34+
large: "object.asteroids.ufo.large",
35+
small: "object.asteroids.ufo.small",
3636
});
37-
const SHIP_OBJECT_VECTOR_ASSET_ID = "asset.asteroids.ship";
37+
const SHIP_OBJECT_VECTOR_OBJECT_ID = "object.asteroids.ship";
3838
const LIFE_ICON_POINTS = [
3939
[14, 0],
4040
[-10, -8],
@@ -735,7 +735,7 @@ export default class AsteroidsGameScene extends Scene {
735735
this.drawObjectVectorAsset(renderer, "asteroids", {
736736
elapsedMs: this.objectVectorPlaybackMs,
737737
fps: 12,
738-
assetId: ASTEROID_OBJECT_VECTOR_ASSET_IDS[asteroid.size],
738+
objectId: ASTEROID_OBJECT_VECTOR_OBJECT_IDS[asteroid.size],
739739
rotation: asteroid.angle,
740740
stateId: "active",
741741
x: asteroid.x,
@@ -747,7 +747,7 @@ export default class AsteroidsGameScene extends Scene {
747747
this.drawObjectVectorAsset(renderer, "ufo", {
748748
elapsedMs: this.objectVectorPlaybackMs,
749749
fps: 12,
750-
assetId: UFO_OBJECT_VECTOR_ASSET_IDS[this.world.ufo.type],
750+
objectId: UFO_OBJECT_VECTOR_OBJECT_IDS[this.world.ufo.type],
751751
stateId: "active",
752752
x: this.world.ufo.x,
753753
y: this.world.ufo.y,
@@ -768,7 +768,7 @@ export default class AsteroidsGameScene extends Scene {
768768
this.drawObjectVectorAsset(renderer, "ship", {
769769
elapsedMs: this.objectVectorPlaybackMs,
770770
fps: 12,
771-
assetId: SHIP_OBJECT_VECTOR_ASSET_ID,
771+
objectId: SHIP_OBJECT_VECTOR_OBJECT_ID,
772772
rotation: this.world.ship.angle + Math.PI / 2,
773773
stateId: this.world.ship.thrusting && this.session.mode === 'playing' ? "thrust" : "idle",
774774
x: this.world.ship.x,

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2890,6 +2890,8 @@ test.describe("Workspace Manager V2 bootstrap", () => {
28902890
await expect(page.locator("#objectVectorStudioV2ObjectGeometrySummary")).toHaveText("(triangle)");
28912891
await expect(page.locator("#objectVectorStudioV2ObjectGeometryName")).toHaveText(/polygon-\d+/);
28922892
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).not.toContainText(/Shape\s*polygon-\d+ \(triangle\)/);
2893+
await expect(page.locator("#objectVectorStudioV2ObjectDetails [data-polygon-side-action]")).toHaveCount(0);
2894+
await expect(page.locator("#objectVectorStudioV2ObjectDetails [data-polygon-point-select='true']")).toHaveCount(0);
28932895

28942896
expect(pageErrors).toEqual([]);
28952897
expect(consoleErrors).toEqual([]);
@@ -3333,6 +3335,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
33333335
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).not.toContainText("ship-template-hull");
33343336
await expect(page.locator("#objectVectorStudioV2ObjectGeometrySummary")).toHaveText("(polygon)");
33353337
await expect(page.locator("#objectVectorStudioV2ObjectGeometryName")).toHaveText("ship-template-hull");
3338+
await expect(page.locator("[aria-label='Frame controls'] button")).toHaveText(["Frame Earlier", "Duplicate Frame", "Frame Later"]);
33363339

33373340
await page.locator("#objectVectorStudioV2DuplicateFrameButton").click();
33383341
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-state-id='idle']")).toHaveCount(2);
@@ -4042,8 +4045,10 @@ test.describe("Workspace Manager V2 bootstrap", () => {
40424045
expect(diagnostics.renderCounts.ufo).toBeGreaterThan(0);
40434046
const eventMessages = diagnostics.events.map((entry) => entry.message).join("\n");
40444047
expect(eventMessages).toContain("Object Vector runtime asset load from Asteroids game.manifest.json:tools.object-vector-studio-v2: 6 objects.");
4045-
expect(eventMessages).toContain("Object Vector runtime asset asset.asteroids.ship resolved to object.asteroids.ship.");
4046-
expect(eventMessages).toContain("Object Vector runtime asset asset.asteroids.ufo.small resolved to object.asteroids.ufo.small.");
4048+
expect(eventMessages).toContain("Object Vector runtime cache miss for object.asteroids.ship; cached resolved object.");
4049+
expect(eventMessages).toContain("Object Vector runtime cache miss for object.asteroids.ufo.small; cached resolved object.");
4050+
expect(eventMessages).not.toContain("Object Vector runtime asset asset.asteroids.ship resolved to object.asteroids.ship.");
4051+
expect(eventMessages).not.toContain("Object Vector runtime asset asset.asteroids.ufo.small resolved to object.asteroids.ufo.small.");
40474052
expect(eventMessages).toContain("Object Vector runtime frame resolved: object.asteroids.ship idle/idle-frame-1.");
40484053
expect(eventMessages).toContain("Object Vector runtime rendered object.asteroids.ship: 1 shapes state=idle frame=idle-frame-1.");
40494054
expect(eventMessages).toContain("Object Vector runtime rendered object.asteroids.ufo.small: 2 shapes state=active frame=active-frame-1.");
@@ -6395,6 +6400,15 @@ test.describe("Workspace Manager V2 bootstrap", () => {
63956400
expect(asteroidsManifest.documentKind).toBeUndefined();
63966401
expect(asteroidsManifest.tools).toBeUndefined();
63976402
expect(asteroidsManifest.game.gameData.launch.directPath).toBe("/games/Asteroids/index.html");
6403+
expect(asteroidsManifest.game.gameData.objectVectorRuntime.objectIds).toEqual({
6404+
asteroidLarge: "object.asteroids.asteroid.large",
6405+
asteroidMedium: "object.asteroids.asteroid.medium",
6406+
asteroidSmall: "object.asteroids.asteroid.small",
6407+
ship: "object.asteroids.ship",
6408+
ufoLarge: "object.asteroids.ufo.large",
6409+
ufoSmall: "object.asteroids.ufo.small"
6410+
});
6411+
expect(Object.values(asteroidsManifest.game.gameData.objectVectorRuntime.objectIds).every((id) => id.startsWith("object."))).toBe(true);
63986412
const manifestWorkspace = asteroidsManifest.game.workspace;
63996413
expect(manifestWorkspace.documentKind).toBe("workspace-manifest");
64006414
expect(Object.keys(manifestWorkspace.tools).sort()).toEqual(expect.arrayContaining(["asset-manager-v2", "object-vector-studio-v2", "palette-manager-v2", "vector-map-editor"]));

tools/object-vector-studio-v2/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
157157
</label>
158158
</div>
159159
<div class="object-vector-studio-v2__animation-controls" aria-label="Frame controls">
160-
<button id="objectVectorStudioV2DuplicateFrameButton" type="button" disabled title="Disabled until a state frame is selected">Duplicate Frame</button>
161160
<button id="objectVectorStudioV2FrameEarlierButton" type="button" disabled title="Disabled until the selected frame can move earlier">Frame Earlier</button>
161+
<button id="objectVectorStudioV2DuplicateFrameButton" type="button" disabled title="Disabled until a state frame is selected">Duplicate Frame</button>
162162
<button id="objectVectorStudioV2FrameLaterButton" type="button" disabled title="Disabled until the selected frame can move later">Frame Later</button>
163163
</div>
164164
<div id="objectVectorStudioV2FrameTimeline" class="object-vector-studio-v2__frame-timeline" aria-label="Frame timeline"></div>

0 commit comments

Comments
 (0)