Skip to content

Commit 2e392a0

Browse files
author
DavidQ
committed
Remove navigation mode switching and enforce workspace-only vs tool-only import export flows - PR_11_276
1 parent 287c786 commit 2e392a0

6 files changed

Lines changed: 115 additions & 183 deletions
Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
# PR_11_276 Workspace V2 Nav Mode Separation + Workspace Export Contract Report
1+
# PR_11_276 Workspace V2 Strict Nav Mode Separation + Export Contract Report
22

33
## Scope
4-
Workspace V2 only:
5-
- import/export controls
6-
- tool-vs-workspace nav mode separation
7-
- workspace export/import contract alignment
4+
Workspace V2 nav/import/export UI only, plus validation that tool pages do not expose workspace controls.
85

96
## Files Changed
107
- tools/workspace-v2/index.html
@@ -15,28 +12,16 @@ Workspace V2 only:
1512
- docs/dev/reports/PR_11_276_workspace_v2_nav_mode_and_export_contract_report.md
1613

1714
## Implementation Summary
18-
- Added explicit nav mode selector:
19-
- `Tool Mode (navTools)`
20-
- `Workspace Mode (navWorkspace)`
21-
- Split UI actions by mode:
22-
- navTools: tool session import/export only
23-
- navWorkspace: workspace session import/export only
24-
- Added mode-gated handler checks so actions cannot run from the wrong mode.
25-
- Tool mode export (`exportCurrentSessionJson`) now exports only the active tool payload JSON.
26-
- Workspace mode export (`exportWorkspaceSessionJson`) now exports portable workspace container contract:
27-
- `version`, `toolId: workspace-v2`
28-
- `workspaceSession` with workspace identity, default/active tool, active host context, `toolSessions` grouped by toolId/sessionId, and `savedSessions`
29-
- Removed runtime-only fields from workspace export payload contract:
30-
- `sessionHistory`
31-
- `sessionSelection`
32-
- `mergeAuditLog`
33-
- lone `activeSessionPayload`
34-
- Added workspace import (`importWorkspaceSessionJson`) with contract validation and state hydration for active session + saved sessions.
35-
36-
## Workspace Contract Alignment Notes
37-
- Existing repo schemas under `tools/schemas/workspace*.json` are workspace/game manifest contracts for legacy tool ids and are not directly compatible with Workspace V2 tool-session lane.
38-
- This PR keeps schema files untouched and enforces a Workspace V2 portable session wrapper contract in code/tests.
39-
- No schema file correction was required for this scoped Workspace V2 runtime/export lane.
15+
- Removed Workspace V2 Navigation Mode dropdown.
16+
- Removed Workspace V2 Tool Mode (navTools) controls from UI.
17+
- Workspace V2 now always runs workspace-context import/export controls only.
18+
- Wired Workspace V2 import/export buttons directly to workspace-session handlers.
19+
- Workspace export remains a portable workspace-session wrapper and excludes runtime-only fields:
20+
- sessionHistory
21+
- sessionSelection
22+
- mergeAuditLog
23+
- lone activeSessionPayload
24+
- Tool pages validated to confirm they do not expose workspace-session import/export controls.
4025

4126
## Validation Commands
4227
1. `node --check tools/workspace-v2/index.js`
@@ -47,6 +32,10 @@ Workspace V2 only:
4732
- PASS
4833
- Results: `tmp/v2-current-session-export-results.json`
4934

35+
## Schema Alignment Note
36+
- Existing repository workspace schema files are legacy workspace/game manifest contracts and were not modified.
37+
- This PR keeps Workspace V2 session import/export validation in runtime code for the Workspace V2 portable session wrapper contract.
38+
5039
## Full Samples Smoke Decision
5140
- Skipped full samples smoke test.
52-
- Reason: changes are confined to Workspace V2 import/export/nav-mode logic and validated with targeted runtime checks.
41+
- Reason: scope is limited to Workspace V2 nav/import/export UI and targeted runtime validation covers the changed behavior.

docs/pr/BUILD_PR_11_276_WORKSPACE_V2_NAV_MODE_SEPARATION_AND_EXPORT_CONTRACT_CORRECTION.md

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# BUILD_PR_11_276_WORKSPACE_V2_NAV_MODE_SEPARATION_AND_EXPORT_CONTRACT_CORRECTION
1+
# BUILD_PR_11_276_WORKSPACE_V2_STRICT_NAV_MODE_SEPARATION
22

33
## Purpose
4-
Implement Workspace V2 nav mode separation for import/export controls and align workspace export/import with a portable workspace-level session contract.
4+
Continue/fix PR_11_276 by enforcing strict Workspace V2 navigation mode separation and workspace-session-only import/export controls.
55

66
## Files
77
- tools/workspace-v2/index.html
@@ -10,31 +10,23 @@ Implement Workspace V2 nav mode separation for import/export controls and align
1010
- docs/dev/reports/PR_11_276_workspace_v2_nav_mode_and_export_contract_report.md
1111

1212
## Implementation
13-
1. Add navigation mode selector in Workspace V2:
14-
- `tools` (navTools)
15-
- `workspace` (navWorkspace)
16-
2. Split Import/Export UI into mode-owned action groups:
17-
- navTools section keeps single-tool import/export controls
18-
- navWorkspace section adds workspace import/export controls
19-
3. Wire mode rendering in JS with explicit show/hide behavior.
20-
4. Enforce mode-gated actions:
21-
- tool import/export blocked outside tool mode
22-
- workspace import/export blocked outside workspace mode
23-
5. Tool mode export remains tool-payload scoped.
24-
6. Workspace mode export outputs portable workspace wrapper containing:
13+
1. Remove nav mode dropdown and all navTools UI controls from Workspace V2.
14+
2. Keep only workspace-session import/export controls in Workspace V2.
15+
3. Wire existing Workspace V2 import/export buttons directly to workspace-session handlers.
16+
4. Keep tool-mode handlers unavailable on Workspace V2 (no mode switching surface).
17+
5. Preserve workspace-session export contract:
2518
- workspace identity/version
2619
- active/default tool identity
27-
- included tool payloads grouped by `toolId` and `sessionId`
28-
- saved session map
29-
- excludes runtime-only fields (`sessionHistory`, `sessionSelection`, `mergeAuditLog`, lone `activeSessionPayload`)
30-
7. Workspace import validates workspace wrapper contract and restores active session + saved sessions to operational state.
31-
8. Add targeted runtime coverage for mode separation and export contract behavior.
20+
- toolSessions grouped by toolId/sessionId
21+
- savedSessions
22+
- excludes runtime-only fields (sessionHistory/sessionSelection/mergeAuditLog/lone activeSessionPayload)
23+
6. Update runtime test to assert strict no-overlap behavior and tool-page workspace-control absence.
3224

3325
## Acceptance
34-
- Tool mode only exposes tool import/export actions.
35-
- Workspace mode only exposes workspace import/export actions.
36-
- Workspace export is portable wrapper contract and excludes runtime-only fields.
37-
- Save/Load/Overwrite/Diff/Merge remain operational after workspace import.
26+
- Workspace V2 has no mode switch and no tool import/export controls.
27+
- Workspace V2 import/export is workspace-session only.
28+
- Tool pages do not expose workspace import/export controls.
29+
- No navTools/navWorkspace overlap on any page.
3830

3931
## Validation
4032
- node --check tools/workspace-v2/index.js

docs/pr/PLAN_PR_11_276_WORKSPACE_V2_NAV_MODE_SEPARATION_AND_EXPORT_CONTRACT_CORRECTION.md

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# PLAN_PR_11_276_WORKSPACE_V2_NAV_MODE_SEPARATION_AND_EXPORT_CONTRACT_CORRECTION
1+
# PLAN_PR_11_276_WORKSPACE_V2_STRICT_NAV_MODE_SEPARATION
22

33
## Purpose
4-
Separate tool-vs-workspace navigation modes in Workspace V2 import/export controls and correct workspace export to a portable workspace-level contract.
4+
Enforce strict Workspace V2 navigation mode separation by removing mode switching and keeping Workspace V2 import/export controls workspace-session only.
55

66
## Scope
77
- tools/workspace-v2/index.html
@@ -10,18 +10,16 @@ Separate tool-vs-workspace navigation modes in Workspace V2 import/export contro
1010
- docs/report only
1111

1212
## Goals
13-
- Add explicit nav mode separation:
14-
- tool mode (navTools) exposes only tool import/export actions
15-
- workspace mode (navWorkspace) exposes only workspace import/export actions
16-
- Keep tool mode import/export tool-session scoped.
17-
- Make workspace mode export a portable workspace wrapper contract.
18-
- Remove runtime-only fields from workspace export payload.
19-
- Keep Save/Load/Overwrite/Diff/Merge operational with imported/exported workspace data.
13+
- Remove/hide navigation mode dropdown in Workspace V2.
14+
- Workspace V2 exposes only navWorkspace import/export controls.
15+
- No Tool Mode controls on Workspace V2.
16+
- Workspace import/export validates workspace-session shape.
17+
- Tool pages do not expose workspace import/export controls.
18+
- No overlap/switching/fallback between navTools and navWorkspace.
2019

2120
## Out of Scope
22-
- No schema file rewrites.
23-
- No unrelated tool changes.
24-
- No merge/diff algorithm refactors.
21+
- No schema rewrites unless strictly required.
22+
- No unrelated tool behavior changes.
2523

2624
## Validation
2725
- node --check tools/workspace-v2/index.js

tests/runtime/V2CurrentSessionExport.test.mjs

Lines changed: 59 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ const workspaceJsPath = path.join(repoRoot, "tools", "workspace-v2", "index.js")
1212
const testPath = path.join(repoRoot, "tests", "runtime", "V2CurrentSessionExport.test.mjs");
1313
const resultsPath = path.join(repoRoot, "tmp", "v2-current-session-export-results.json");
1414

15+
const TOOL_IDS = [
16+
"asset-browser-v2",
17+
"palette-manager-v2",
18+
"svg-asset-studio-v2",
19+
"tilemap-studio-v2",
20+
"vector-map-editor-v2"
21+
];
22+
1523
function checkSyntax(filePath) {
1624
try {
1725
execFileSync(process.execPath, ["--check", filePath], {
@@ -24,28 +32,6 @@ function checkSyntax(filePath) {
2432
}
2533
}
2634

27-
function simulateToolModeExport(activePayload, currentHostContextId) {
28-
if (!activePayload || typeof activePayload !== "object" || Array.isArray(activePayload)) {
29-
return {
30-
ok: false,
31-
status: "No active Workspace V2 session is available to export.",
32-
filename: "",
33-
serialized: ""
34-
};
35-
}
36-
const payloadToolId = typeof activePayload.toolId === "string" ? activePayload.toolId.trim() : "";
37-
const toolIdForFile = payloadToolId || "workspace-v2";
38-
const sessionIdForFile = typeof currentHostContextId === "string" && currentHostContextId.trim()
39-
? currentHostContextId.trim()
40-
: "session";
41-
return {
42-
ok: true,
43-
status: "Exported current workspace session JSON.",
44-
filename: `${toolIdForFile}-${sessionIdForFile}.json`,
45-
serialized: JSON.stringify(activePayload, null, 2)
46-
};
47-
}
48-
4935
function simulateWorkspaceModeExport(activePayload, currentHostContextId, library) {
5036
if (!activePayload || typeof activePayload !== "object" || Array.isArray(activePayload)) {
5137
return {
@@ -116,37 +102,69 @@ export function run() {
116102
if (!workspaceJsSyntax.ok) failures.push("tools/workspace-v2/index.js failed syntax check.");
117103
if (!testSyntax.ok) failures.push("tests/runtime/V2CurrentSessionExport.test.mjs failed syntax check.");
118104

119-
const requiredHtmlTokens = [
105+
const requiredWorkspaceHtmlTokens = [
106+
'id="workspaceV2ImportJson"',
107+
'id="workspaceV2ImportButton"',
108+
'id="workspaceV2ExportButton"',
109+
"Import Workspace Session JSON",
110+
"Export Workspace Session JSON"
111+
];
112+
requiredWorkspaceHtmlTokens.forEach((token) => {
113+
if (!workspaceHtml.includes(token)) {
114+
failures.push(`Missing required workspace import/export token: ${token}`);
115+
}
116+
});
117+
118+
const forbiddenWorkspaceHtmlTokens = [
120119
'id="workspaceV2NavModeSelect"',
121120
'id="workspaceV2NavToolsActions"',
122121
'id="workspaceV2NavWorkspaceActions"',
123-
'id="workspaceV2ImportWorkspaceButton"',
124-
'id="workspaceV2ExportWorkspaceButton"'
122+
"Tool Mode (navTools)",
123+
"Import Tool Session JSON",
124+
"Export Tool Session JSON"
125125
];
126-
requiredHtmlTokens.forEach((token) => {
127-
if (!workspaceHtml.includes(token)) {
128-
failures.push(`Missing nav mode separation HTML token: ${token}`);
126+
forbiddenWorkspaceHtmlTokens.forEach((token) => {
127+
if (workspaceHtml.includes(token)) {
128+
failures.push(`Workspace V2 should not expose mode-switch/tool-mode control: ${token}`);
129129
}
130130
});
131131

132-
const requiredJsTokens = [
133-
"currentNavMode()",
134-
"renderNavModeActions()",
132+
const requiredWorkspaceJsTokens = [
135133
"importWorkspaceSessionJson()",
136134
"exportWorkspaceSessionJson()",
137135
"buildPortableWorkspaceSessionContainer()",
138-
"if (!this.inToolsNavMode())",
139-
"if (!this.inWorkspaceNavMode())",
136+
"runtime-only fields are not allowed in portable workspace payload",
140137
"toolSessions",
141-
"savedSessions",
142-
"runtime-only fields are not allowed in portable workspace payload"
138+
"savedSessions"
143139
];
144-
requiredJsTokens.forEach((token) => {
140+
requiredWorkspaceJsTokens.forEach((token) => {
145141
if (!workspaceJs.includes(token)) {
146-
failures.push(`Missing nav/export contract JS token: ${token}`);
142+
failures.push(`Missing required workspace contract JS token: ${token}`);
147143
}
148144
});
149145

146+
TOOL_IDS.forEach((toolId) => {
147+
const toolHtmlPath = path.join(repoRoot, "tools", toolId, "index.html");
148+
const toolHtmlExists = fs.existsSync(toolHtmlPath);
149+
if (!toolHtmlExists) {
150+
failures.push(`Missing tool page HTML: tools/${toolId}/index.html`);
151+
return;
152+
}
153+
const toolHtml = fs.readFileSync(toolHtmlPath, "utf8");
154+
const forbiddenToolTokens = [
155+
'id="workspaceV2ImportJson"',
156+
'id="workspaceV2ImportButton"',
157+
'id="workspaceV2ExportButton"',
158+
"Import Workspace Session JSON",
159+
"Export Workspace Session JSON"
160+
];
161+
forbiddenToolTokens.forEach((token) => {
162+
if (toolHtml.includes(token)) {
163+
failures.push(`Tool page should not expose workspace import/export controls (${toolId}): ${token}`);
164+
}
165+
});
166+
});
167+
150168
const activePayload = {
151169
version: "v2",
152170
toolId: "palette-manager-v2",
@@ -159,27 +177,13 @@ export function run() {
159177
payloadJson: { assetCatalog: { entries: [{ id: "asset-1" }] } }
160178
}
161179
};
162-
163-
const toolExport = simulateToolModeExport(activePayload, "palette-manager-v2-1234567890123-abcd1234");
164-
if (!toolExport.ok) failures.push("Tool mode export should succeed when active payload exists.");
165-
if (toolExport.filename !== "palette-manager-v2-palette-manager-v2-1234567890123-abcd1234.json") {
166-
failures.push("Tool mode export filename mismatch.");
167-
}
168-
const toolExportParsed = toolExport.serialized ? JSON.parse(toolExport.serialized) : null;
169-
if (JSON.stringify(toolExportParsed) !== JSON.stringify(activePayload)) {
170-
failures.push("Tool mode export should preserve active tool payload exactly.");
171-
}
172-
173180
const workspaceExport = simulateWorkspaceModeExport(activePayload, "palette-manager-v2-1234567890123-abcd1234", library);
174-
if (!workspaceExport.ok) failures.push("Workspace mode export should succeed when active payload exists.");
175-
if (workspaceExport.filename !== "workspace-v2-palette-manager-v2-palette-manager-v2-1234567890123-abcd1234.json") {
176-
failures.push("Workspace mode export filename mismatch.");
177-
}
181+
if (!workspaceExport.ok) failures.push("Workspace export should succeed when active payload exists.");
178182
const workspaceExportParsed = workspaceExport.serialized ? JSON.parse(workspaceExport.serialized) : null;
179183
if (!workspaceExportParsed || typeof workspaceExportParsed !== "object" || Array.isArray(workspaceExportParsed)) {
180-
failures.push("Workspace mode export should be an object container.");
184+
failures.push("Workspace export should be an object container.");
181185
} else if (!workspaceExportParsed.workspaceSession || typeof workspaceExportParsed.workspaceSession !== "object") {
182-
failures.push("Workspace mode export should include workspaceSession container.");
186+
failures.push("Workspace export should include workspaceSession container.");
183187
} else {
184188
if (Object.prototype.hasOwnProperty.call(workspaceExportParsed.workspaceSession, "sessionHistory")) {
185189
failures.push("Workspace export must not include sessionHistory runtime field.");
@@ -193,21 +197,13 @@ export function run() {
193197
if (Object.prototype.hasOwnProperty.call(workspaceExportParsed.workspaceSession, "activeSessionPayload")) {
194198
failures.push("Workspace export must not include lone activeSessionPayload runtime field.");
195199
}
196-
if (JSON.stringify(workspaceExportParsed.workspaceSession.savedSessions) !== JSON.stringify(library)) {
197-
failures.push("Workspace export should preserve savedSessions payload map.");
198-
}
199200
const activeEntry =
200201
workspaceExportParsed.workspaceSession.toolSessions?.["palette-manager-v2"]?.["palette-manager-v2-1234567890123-abcd1234"];
201202
if (JSON.stringify(activeEntry) !== JSON.stringify(activePayload)) {
202203
failures.push("Workspace export should preserve active payload under toolSessions by tool/session id.");
203204
}
204205
}
205206

206-
const noActiveToolExport = simulateToolModeExport(null, "");
207-
if (noActiveToolExport.ok) failures.push("Tool export should fail when active payload is missing.");
208-
const noActiveWorkspaceExport = simulateWorkspaceModeExport(null, "", {});
209-
if (noActiveWorkspaceExport.ok) failures.push("Workspace export should fail when active payload is missing.");
210-
211207
fs.mkdirSync(path.dirname(resultsPath), { recursive: true });
212208
fs.writeFileSync(resultsPath, `${JSON.stringify({
213209
generatedAt: new Date().toISOString(),
@@ -219,10 +215,7 @@ export function run() {
219215
testSyntax
220216
},
221217
scenarios: {
222-
toolExport,
223-
workspaceExport,
224-
noActiveToolExport,
225-
noActiveWorkspaceExport
218+
workspaceExport
226219
}
227220
}, null, 2)}\n`, "utf8");
228221

tools/workspace-v2/index.html

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,30 +35,14 @@ <h2>Producer</h2>
3535

3636
<section class="hub-panel">
3737
<h2>Import / Export Session JSON</h2>
38-
<label for="workspaceV2NavModeSelect">Navigation Mode</label>
39-
<select id="workspaceV2NavModeSelect">
40-
<option value="tools" selected>Tool Mode (navTools)</option>
41-
<option value="workspace">Workspace Mode (navWorkspace)</option>
42-
</select>
43-
<div id="workspaceV2NavToolsActions">
44-
<p>Tool mode actions (navTools): import/export a single active tool session payload.</p>
45-
<label for="workspaceV2ImportJson">Tool Session JSON</label>
46-
<textarea id="workspaceV2ImportJson" rows="12" spellcheck="false" placeholder='{"toolId":"asset-browser-v2","payloadJson":{}}'></textarea>
47-
<label for="workspaceV2ImportFile">Import File</label>
48-
<input id="workspaceV2ImportFile" type="file" accept="application/json,.json" />
49-
<div>
50-
<button id="workspaceV2ImportButton" type="button">Import Tool Session JSON</button>
51-
<button id="workspaceV2ExportButton" type="button">Export Tool Session JSON</button>
52-
</div>
53-
</div>
54-
<div id="workspaceV2NavWorkspaceActions" hidden>
55-
<p>Workspace mode actions (navWorkspace): import/export a portable Workspace V2 session manifest.</p>
56-
<label for="workspaceV2WorkspaceJson">Workspace Session JSON</label>
57-
<textarea id="workspaceV2WorkspaceJson" rows="12" spellcheck="false" placeholder='{"version":"v2","toolId":"workspace-v2","workspaceSession":{}}'></textarea>
58-
<div>
59-
<button id="workspaceV2ImportWorkspaceButton" type="button">Import Workspace Session JSON</button>
60-
<button id="workspaceV2ExportWorkspaceButton" type="button">Export Workspace Session JSON</button>
61-
</div>
38+
<p>Workspace mode actions (navWorkspace): import/export a portable Workspace V2 session manifest.</p>
39+
<label for="workspaceV2ImportJson">Workspace Session JSON</label>
40+
<textarea id="workspaceV2ImportJson" rows="12" spellcheck="false" placeholder='{"version":"v2","toolId":"workspace-v2","workspaceSession":{}}'></textarea>
41+
<label for="workspaceV2ImportFile">Import File</label>
42+
<input id="workspaceV2ImportFile" type="file" accept="application/json,.json" />
43+
<div>
44+
<button id="workspaceV2ImportButton" type="button">Import Workspace Session JSON</button>
45+
<button id="workspaceV2ExportButton" type="button">Export Workspace Session JSON</button>
6246
</div>
6347
</section>
6448

0 commit comments

Comments
 (0)