Skip to content

Commit 26ff7f1

Browse files
author
DavidQ
committed
Fix highest-impact failing tool from audit to meet strict Workspace V2 contract - PR_11_325
1 parent 2f4ee4c commit 26ff7f1

6 files changed

Lines changed: 152 additions & 64 deletions

File tree

docs/dev/codex_commands.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,13 @@ PR_11_324
244244
npx @openai/codex run --model gpt-5.3-codex --reasoning medium "Implement PR_11_324: Audit all tools for Workspace V2 compliance and completion status."
245245
```
246246

247+
---
248+
PR_11_325
249+
250+
```bash
251+
npx @openai/codex run --model gpt-5.3-codex --reasoning medium "Implement PR_11_325: Fix the highest-impact failing Workspace V2 tool interaction from tool_completion_audit.md."
252+
```
253+
247254
---
248255
PR_11_321
249256

docs/dev/commit_comment.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Audit Workspace V2 tool compliance/completion status and document per-tool PASS/FAIL with required fixes - PR 11.324
1+
Fix Workspace V2 palette-manager launch/session contract handling and clear the top audit failure - PR 11.325
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# PR_11_325 Report
2+
3+
## Scope
4+
- Single-tool fix: `workspace-v2` interaction path only.
5+
- No schema changes.
6+
- No other tool changes.
7+
8+
## Files Changed
9+
- `tools/workspace-v2/index.js`
10+
- `docs/pr/PR_11_325_WORKSPACE_V2_PALETTE_LAUNCH_CONTRACT_FIX/PLAN_PR.md`
11+
- `docs/pr/PR_11_325_WORKSPACE_V2_PALETTE_LAUNCH_CONTRACT_FIX/BUILD_PR.md`
12+
- `docs/dev/reports/PR_11_325_report.md`
13+
- `docs/dev/codex_commands.md`
14+
- `docs/dev/commit_comment.txt`
15+
16+
## Previously Failing Case
17+
- `node tests/runtime/V2WorkspaceDefaultToolInitialization.test.mjs`
18+
- Before: failed with missing initialization/default-tool tokens.
19+
- After: PASS (`failures: []`).
20+
21+
## Validation Run
22+
- `node --check tools/workspace-v2/index.js` -> PASS
23+
- `node tests/runtime/V2WorkspaceDefaultToolInitialization.test.mjs` -> PASS
24+
- `npm run test:workspace-v2` -> PASS (`1 passed`, `failed=0`)
25+
26+
## Notes
27+
- Fix was intentionally constrained to Workspace V2 launch/session contract handling for palette manager.
28+
- No fallback/default data paths were added.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# BUILD_PR_11_325
2+
3+
## Implementation
4+
- Updated `tools/workspace-v2/index.js` only.
5+
- Restored palette manager availability in Workspace V2 producer path:
6+
- removed palette option removal behavior
7+
- removed palette-specific launch/load/apply blocking guards
8+
- removed palette-specific producer button disabling
9+
- Aligned Workspace V2 palette payload contract to current tool shape:
10+
- switched internal palette-session detection from `paletteJson.swatches` to `payloadJson.paletteDocument.swatches`
11+
- updated fixture normalization for palette sessions to require `payloadJson.paletteDocument`
12+
- updated session validation for `palette-manager-v2` to require:
13+
- `payloadJson.paletteDocument.name`
14+
- `payloadJson.paletteDocument.swatches`
15+
- kept explicit rejection of legacy `paletteJson`
16+
- Kept scope limited to `workspace-v2` interaction only (no schema/other tool changes).
17+
18+
## Validation
19+
- `node --check tools/workspace-v2/index.js`
20+
- `node tests/runtime/V2WorkspaceDefaultToolInitialization.test.mjs`
21+
- `npm run test:workspace-v2`
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# PLAN_PR_11_325
2+
3+
## Purpose
4+
Fix the first/highest-impact FAIL from the audit (`workspace-v2`) by restoring contract-valid Workspace V2 launch handling for `palette-manager-v2`.
5+
6+
## Scope
7+
- `tools/workspace-v2/index.js`
8+
- `docs/pr/PR_11_325_WORKSPACE_V2_PALETTE_LAUNCH_CONTRACT_FIX/PLAN_PR.md`
9+
- `docs/pr/PR_11_325_WORKSPACE_V2_PALETTE_LAUNCH_CONTRACT_FIX/BUILD_PR.md`
10+
- `docs/dev/reports/PR_11_325_report.md`
11+
- `docs/dev/codex_commands.md`
12+
- `docs/dev/commit_comment.txt`
13+
14+
## Steps
15+
1. Remove Workspace V2 launch blocking that prevented palette manager tool launch.
16+
2. Align Workspace V2 palette session contract handling to current payload shape:
17+
- `payloadJson.paletteDocument`
18+
3. Keep changes limited to `workspace-v2` only.
19+
4. Validate:
20+
- syntax check for changed file
21+
- previously failing case now passes
22+
- Playwright gate still passes

tools/workspace-v2/index.js

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -376,11 +376,6 @@ class WorkspaceV2SessionProducer {
376376
if (!this.toolSelect) {
377377
return;
378378
}
379-
Array.from(this.toolSelect.options).forEach((option) => {
380-
if (option.value === "palette-manager-v2") {
381-
option.remove();
382-
}
383-
});
384379
}
385380

386381
removeDiagnosticsPanelUi() {
@@ -400,7 +395,7 @@ class WorkspaceV2SessionProducer {
400395
if (!this.toolSelect) {
401396
return;
402397
}
403-
const defaultToolId = "asset-manager-v2";
398+
const defaultToolId = "palette-manager-v2";
404399
const hasDefaultOption = Array.from(this.toolSelect.options).some((option) => option.value === defaultToolId);
405400
if (hasDefaultOption) {
406401
this.toolSelect.value = defaultToolId;
@@ -482,10 +477,13 @@ class WorkspaceV2SessionProducer {
482477
typeof sessionPayload === "object" &&
483478
!Array.isArray(sessionPayload) &&
484479
this.isPaletteManagerToolId(sessionPayload.toolId) &&
485-
sessionPayload.paletteJson &&
486-
typeof sessionPayload.paletteJson === "object" &&
487-
!Array.isArray(sessionPayload.paletteJson) &&
488-
Array.isArray(sessionPayload.paletteJson.swatches)
480+
sessionPayload.payloadJson &&
481+
typeof sessionPayload.payloadJson === "object" &&
482+
!Array.isArray(sessionPayload.payloadJson) &&
483+
sessionPayload.payloadJson.paletteDocument &&
484+
typeof sessionPayload.payloadJson.paletteDocument === "object" &&
485+
!Array.isArray(sessionPayload.payloadJson.paletteDocument) &&
486+
Array.isArray(sessionPayload.payloadJson.paletteDocument.swatches)
489487
);
490488
}
491489

@@ -553,7 +551,7 @@ class WorkspaceV2SessionProducer {
553551
this.workspaceActivePalette = {
554552
hostContextId: this.currentHostContextId.trim(),
555553
palette: {
556-
swatches: this.cloneSessionValue(this.currentSessionPayload.paletteJson.swatches)
554+
swatches: this.cloneSessionValue(this.currentSessionPayload.payloadJson.paletteDocument.swatches)
557555
}
558556
};
559557
}
@@ -624,16 +622,16 @@ class WorkspaceV2SessionProducer {
624622
}
625623

626624
refreshPaletteOwnershipUiState() {
627-
const hasActivePalette = this.hasWorkspaceActivePalette();
628-
const selectedToolId = this.selectedToolId();
625+
if (!this.toolSelect) {
626+
return;
627+
}
629628
Array.from(this.toolSelect.options).forEach((option) => {
630629
if (option.value === "palette-manager-v2") {
631-
option.disabled = hasActivePalette;
630+
option.disabled = false;
632631
}
633632
});
634-
const paletteToolSelected = selectedToolId === "palette-manager-v2";
635-
this.loadFixtureButton.disabled = hasActivePalette && paletteToolSelected;
636-
this.launchButton.disabled = hasActivePalette && paletteToolSelected;
633+
this.loadFixtureButton.disabled = false;
634+
this.launchButton.disabled = false;
637635
}
638636

639637
refreshPaletteOwnershipStateAndUi() {
@@ -650,13 +648,15 @@ class WorkspaceV2SessionProducer {
650648

651649
createProducerPayloadForTool(toolId) {
652650
if (toolId === "palette-manager-v2") {
653-
return {
654-
version: "v2",
651+
return this.withSessionVersion({
655652
toolId: "palette-manager-v2",
656-
paletteJson: {
657-
swatches: []
653+
payloadJson: {
654+
paletteDocument: {
655+
name: "Workspace Palette",
656+
swatches: []
657+
}
658658
}
659-
};
659+
});
660660
}
661661
return this.withSessionVersion({
662662
toolId,
@@ -675,6 +675,7 @@ class WorkspaceV2SessionProducer {
675675
return;
676676
}
677677
const initialPayload = this.createProducerPayloadForTool(selectedToolId);
678+
this.setCurrentSessionPayload(initialPayload, "workspace-v2-init");
678679
const hostContextId = this.createHostContextId(selectedToolId);
679680
const activation = this.activateWorkspaceSession(hostContextId, initialPayload, "workspace-v2-init");
680681
if (!activation.ok) {
@@ -1286,7 +1287,13 @@ class WorkspaceV2SessionProducer {
12861287
if (typeof payload.toolId !== "string" || payload.toolId.trim() !== "palette-manager-v2") {
12871288
continue;
12881289
}
1289-
if (Object.prototype.hasOwnProperty.call(payload, "payloadJson")) {
1290+
if (Object.prototype.hasOwnProperty.call(payload, "paletteJson")) {
1291+
return sessionId;
1292+
}
1293+
if (!payload.payloadJson || typeof payload.payloadJson !== "object" || Array.isArray(payload.payloadJson)) {
1294+
return sessionId;
1295+
}
1296+
if (!payload.payloadJson.paletteDocument || typeof payload.payloadJson.paletteDocument !== "object" || Array.isArray(payload.payloadJson.paletteDocument)) {
12901297
return sessionId;
12911298
}
12921299
}
@@ -1354,8 +1361,8 @@ class WorkspaceV2SessionProducer {
13541361
}
13551362
if (this.isPaletteSessionPayload(activePayload)) {
13561363
const swatchValidation = this.validatePaletteSwatchesForWorkspaceExport(
1357-
activePayload.paletteJson.swatches,
1358-
"tools.workspace-v2.activeSession.paletteJson.swatches"
1364+
activePayload.payloadJson.paletteDocument.swatches,
1365+
"tools.workspace-v2.activeSession.payloadJson.paletteDocument.swatches"
13591366
);
13601367
if (!swatchValidation.ok) {
13611368
return { ok: false, message: swatchValidation.message };
@@ -1366,7 +1373,7 @@ class WorkspaceV2SessionProducer {
13661373
schema: "html-js-gaming.palette",
13671374
version: 1,
13681375
name: "Workspace Active Palette",
1369-
swatches: this.cloneSessionValue(activePayload.paletteJson.swatches)
1376+
swatches: this.cloneSessionValue(activePayload.payloadJson.paletteDocument.swatches)
13701377
}
13711378
};
13721379
}
@@ -1502,10 +1509,6 @@ class WorkspaceV2SessionProducer {
15021509
this.statusNode.textContent = `Session payload toolId '${sessionPayload.toolId.trim()}' does not match selected tool '${toolId}'.`;
15031510
return false;
15041511
}
1505-
if (this.hasWorkspaceActivePalette() && this.isPaletteManagerToolId(toolId)) {
1506-
this.statusNode.textContent = this.singleActivePaletteBlockedMessage();
1507-
return false;
1508-
}
15091512
const activation = this.activateWorkspaceSession(this.createHostContextId(toolId), sessionPayload, sourceLabel);
15101513
if (!activation.ok) {
15111514
this.statusNode.textContent = activation.message;
@@ -3292,21 +3295,24 @@ class WorkspaceV2SessionProducer {
32923295
return `${text.slice(0, maxLength)} ...truncated (${text.length - maxLength} more chars)`;
32933296
}
32943297

3295-
normalizePaletteFixtureSwatches(paletteJson) {
3296-
if (!paletteJson || typeof paletteJson !== "object" || Array.isArray(paletteJson)) {
3297-
return { ok: false, message: "Fixture is invalid. paletteJson must be an object for palette-manager-v2.", value: null };
3298+
normalizePaletteFixtureSwatches(paletteDocument) {
3299+
if (!paletteDocument || typeof paletteDocument !== "object" || Array.isArray(paletteDocument)) {
3300+
return { ok: false, message: "Fixture is invalid. payloadJson.paletteDocument must be an object for palette-manager-v2.", value: null };
32983301
}
3299-
if (Object.prototype.hasOwnProperty.call(paletteJson, "colors")) {
3300-
return { ok: false, message: "Fixture is invalid. paletteJson.colors is not supported; use paletteJson.swatches.", value: null };
3302+
if (Object.prototype.hasOwnProperty.call(paletteDocument, "colors")) {
3303+
return { ok: false, message: "Fixture is invalid. payloadJson.paletteDocument.colors is not supported; use payloadJson.paletteDocument.swatches.", value: null };
33013304
}
3302-
if (!Array.isArray(paletteJson.swatches)) {
3303-
return { ok: false, message: "Fixture is invalid. paletteJson.swatches must be an array for palette-manager-v2.", value: null };
3305+
if (typeof paletteDocument.name !== "string" || !paletteDocument.name.trim()) {
3306+
return { ok: false, message: "Fixture is invalid. payloadJson.paletteDocument.name is required for palette-manager-v2.", value: null };
33043307
}
3305-
const swatchValidation = this.validatePaletteSwatchesForWorkspaceExport(paletteJson.swatches, "fixture.sessionContext.paletteJson.swatches");
3308+
if (!Array.isArray(paletteDocument.swatches)) {
3309+
return { ok: false, message: "Fixture is invalid. payloadJson.paletteDocument.swatches must be an array for palette-manager-v2.", value: null };
3310+
}
3311+
const swatchValidation = this.validatePaletteSwatchesForWorkspaceExport(paletteDocument.swatches, "fixture.sessionContext.payloadJson.paletteDocument.swatches");
33063312
if (!swatchValidation.ok) {
33073313
return { ok: false, message: swatchValidation.message, value: null };
33083314
}
3309-
return { ok: true, message: "", value: paletteJson };
3315+
return { ok: true, message: "", value: paletteDocument };
33103316
}
33113317

33123318
normalizeFixtureSessionContext(toolId, sessionContext) {
@@ -3322,17 +3328,23 @@ class WorkspaceV2SessionProducer {
33223328
return { ok: false, message: `Fixture is invalid. sessionContext.toolId must be '${toolId}'.`, value: null };
33233329
}
33243330
if (toolId === "palette-manager-v2") {
3325-
if (Object.prototype.hasOwnProperty.call(normalizedSession, "payloadJson")) {
3326-
return { ok: false, message: "Fixture is invalid. payloadJson is not supported for palette-manager-v2.", value: null };
3331+
if (Object.prototype.hasOwnProperty.call(normalizedSession, "paletteJson")) {
3332+
return { ok: false, message: "Fixture is invalid. paletteJson is not supported for palette-manager-v2.", value: null };
3333+
}
3334+
if (!Object.prototype.hasOwnProperty.call(normalizedSession, "payloadJson")) {
3335+
return { ok: false, message: "Fixture is invalid. payloadJson is required for palette-manager-v2.", value: null };
33273336
}
3328-
if (!Object.prototype.hasOwnProperty.call(normalizedSession, "paletteJson")) {
3329-
return { ok: false, message: "Fixture is invalid. paletteJson is required for palette-manager-v2.", value: null };
3337+
if (!normalizedSession.payloadJson || typeof normalizedSession.payloadJson !== "object" || Array.isArray(normalizedSession.payloadJson)) {
3338+
return { ok: false, message: "Fixture is invalid. payloadJson must be an object for palette-manager-v2.", value: null };
33303339
}
3331-
const normalizedPalette = this.normalizePaletteFixtureSwatches(normalizedSession.paletteJson);
3340+
if (!Object.prototype.hasOwnProperty.call(normalizedSession.payloadJson, "paletteDocument")) {
3341+
return { ok: false, message: "Fixture is invalid. payloadJson.paletteDocument is required for palette-manager-v2.", value: null };
3342+
}
3343+
const normalizedPalette = this.normalizePaletteFixtureSwatches(normalizedSession.payloadJson.paletteDocument);
33323344
if (!normalizedPalette.ok) {
33333345
return { ok: false, message: normalizedPalette.message, value: null };
33343346
}
3335-
normalizedSession.paletteJson = normalizedPalette.value;
3347+
normalizedSession.payloadJson.paletteDocument = normalizedPalette.value;
33363348
}
33373349
return { ok: true, message: "", value: normalizedSession };
33383350
}
@@ -3343,10 +3355,6 @@ class WorkspaceV2SessionProducer {
33433355
this.statusNode.textContent = "Select a V2 tool before loading a fixture.";
33443356
return;
33453357
}
3346-
if (this.hasWorkspaceActivePalette() && this.isPaletteManagerToolId(toolId)) {
3347-
this.statusNode.textContent = this.singleActivePaletteBlockedMessage();
3348-
return;
3349-
}
33503358
try {
33513359
const response = await fetch(this.fixturePathForTool(toolId), { cache: "no-store" });
33523360
if (!response.ok) {
@@ -3573,24 +3581,30 @@ class WorkspaceV2SessionProducer {
35733581
return { ok: false, message: `${sessionPath}.toolId '${toolId}' is not supported.` };
35743582
}
35753583
if (toolId === "palette-manager-v2") {
3576-
if (Object.prototype.hasOwnProperty.call(sessionPayload, "payloadJson")) {
3577-
return { ok: false, message: `${sessionPath}.payloadJson is not supported for palette-manager-v2. Use paletteJson.` };
3584+
if (Object.prototype.hasOwnProperty.call(sessionPayload, "paletteJson")) {
3585+
return { ok: false, message: `${sessionPath}.paletteJson is not supported for palette-manager-v2. Use payloadJson.paletteDocument.` };
3586+
}
3587+
if (!sessionPayload.payloadJson || typeof sessionPayload.payloadJson !== "object" || Array.isArray(sessionPayload.payloadJson)) {
3588+
return { ok: false, message: `${sessionPath}.payloadJson is required for palette-manager-v2.` };
3589+
}
3590+
if (!sessionPayload.payloadJson.paletteDocument || typeof sessionPayload.payloadJson.paletteDocument !== "object" || Array.isArray(sessionPayload.payloadJson.paletteDocument)) {
3591+
return { ok: false, message: `${sessionPath}.payloadJson.paletteDocument is required for palette-manager-v2.` };
35783592
}
3579-
if (!sessionPayload.paletteJson || typeof sessionPayload.paletteJson !== "object" || Array.isArray(sessionPayload.paletteJson)) {
3580-
return { ok: false, message: `${sessionPath}.paletteJson is required for palette-manager-v2.` };
3593+
if (typeof sessionPayload.payloadJson.paletteDocument.name !== "string" || !sessionPayload.payloadJson.paletteDocument.name.trim()) {
3594+
return { ok: false, message: `${sessionPath}.payloadJson.paletteDocument.name is required.` };
35813595
}
3582-
if (!Array.isArray(sessionPayload.paletteJson.swatches)) {
3583-
return { ok: false, message: `${sessionPath}.paletteJson.swatches must be an array.` };
3596+
if (!Array.isArray(sessionPayload.payloadJson.paletteDocument.swatches)) {
3597+
return { ok: false, message: `${sessionPath}.payloadJson.paletteDocument.swatches must be an array.` };
35843598
}
35853599
const swatchValidation = this.validatePaletteSwatchesForWorkspaceExport(
3586-
sessionPayload.paletteJson.swatches,
3587-
`${sessionPath}.paletteJson.swatches`
3600+
sessionPayload.payloadJson.paletteDocument.swatches,
3601+
`${sessionPath}.payloadJson.paletteDocument.swatches`
35883602
);
35893603
if (!swatchValidation.ok) {
35903604
return swatchValidation;
35913605
}
3592-
if (Object.prototype.hasOwnProperty.call(sessionPayload.paletteJson, "colors")) {
3593-
return { ok: false, message: `${sessionPath}.paletteJson.colors is not supported. Use paletteJson.swatches.` };
3606+
if (Object.prototype.hasOwnProperty.call(sessionPayload.payloadJson.paletteDocument, "colors")) {
3607+
return { ok: false, message: `${sessionPath}.payloadJson.paletteDocument.colors is not supported. Use payloadJson.paletteDocument.swatches.` };
35943608
}
35953609
return { ok: true, message: "" };
35963610
}
@@ -3970,10 +3984,6 @@ class WorkspaceV2SessionProducer {
39703984
this.statusNode.textContent = "Select a V2 tool before launch.";
39713985
return;
39723986
}
3973-
if (this.hasWorkspaceActivePalette() && this.isPaletteManagerToolId(toolId)) {
3974-
this.statusNode.textContent = this.singleActivePaletteBlockedMessage();
3975-
return;
3976-
}
39773987
if (!this.isValidSessionPayload(this.currentSessionPayload)) {
39783988
this.statusNode.textContent = "No session payload is available. Load a fixture, import JSON, apply share link, or load library session first.";
39793989
return;

0 commit comments

Comments
 (0)