Skip to content

Commit 35c7b82

Browse files
author
DavidQ
committed
Close out Workspace V2 session tools with bundled UX and state consistency fixes - PR_11_266
1 parent a9e6f1a commit 35c7b82

5 files changed

Lines changed: 376 additions & 7 deletions
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# PR_11_266 Workspace V2 Session Tools Closeout Bundle Report
2+
3+
## Scope
4+
Workspace V2 only:
5+
- session library
6+
- session diff
7+
- session merge
8+
- session delete/refresh
9+
- session status/empty/error UX
10+
11+
## Files Changed
12+
- tools/workspace-v2/index.js
13+
- tests/runtime/V2SessionToolsCloseoutBundle.test.mjs
14+
- docs/pr/PLAN_PR_11_266_WORKSPACE_V2_SESSION_TOOLS_CLOSEOUT_BUNDLE.md
15+
- docs/pr/BUILD_PR_11_266_WORKSPACE_V2_SESSION_TOOLS_CLOSEOUT_BUNDLE.md
16+
- docs/dev/reports/PR_11_266_session_tools_closeout_bundle_report.md
17+
18+
## Audit + Fix Summary
19+
- Audited Workspace V2 session flows touched in PR_11_252 through PR_11_265.
20+
- Fixed stale diff output/state by adding explicit diff selection-key tracking (`diffOutputSelectionKey`).
21+
- Added explicit diff selection-change handling:
22+
- clears stale diff output when selection pair changes
23+
- shows clear current-state messages for missing/same/ready selection states
24+
- Added explicit merge selection-change status feedback:
25+
- missing selection messaging
26+
- same-selection block messaging
27+
- ready-to-preview / ready-to-confirm / ready-to-apply messaging
28+
- Improved refresh consistency:
29+
- refresh action now clears stale diff output state and sets one current-state refresh message
30+
- Improved delete-state consistency:
31+
- recent-session delete now clears stale diff output when deleted session participated in current diff selection/output
32+
- Improved invalid diff-path UX:
33+
- invalid diff payload now sets both output and global status message
34+
- Preserved deterministic state model behavior from PR_11_264/265:
35+
- button enable/disable state remains derived through `refreshWorkspaceSessionUiStateModel(...)`
36+
- no fallback/default data added
37+
38+
## Validation Commands
39+
1. `node --check tools/workspace-v2/index.js`
40+
- PASS
41+
2. `node --check tests/runtime/V2SessionToolsCloseoutBundle.test.mjs`
42+
- PASS
43+
3. `node --check tests/runtime/V2DeterministicStateTransitions.test.mjs`
44+
- PASS
45+
4. `node --check tests/runtime/V2SessionStateModelConsolidation.test.mjs`
46+
- PASS
47+
5. `node tests/runtime/V2SessionToolsCloseoutBundle.test.mjs`
48+
- PASS
49+
- Results: `tmp/v2-session-tools-closeout-results.json`
50+
6. `node tests/runtime/V2DeterministicStateTransitions.test.mjs`
51+
- PASS
52+
- Results: `tmp/v2-deterministic-state-transitions-results.json`
53+
7. `node tests/runtime/V2SessionStateModelConsolidation.test.mjs`
54+
- PASS
55+
- Results: `tmp/v2-session-state-model-consolidation-results.json`
56+
57+
## Full Samples Smoke Decision
58+
- Full samples smoke test was skipped.
59+
- Reason: changes are tightly scoped to Workspace V2 session UX/state handling and were covered by targeted runtime tests.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# BUILD_PR_11_266_WORKSPACE_V2_SESSION_TOOLS_CLOSEOUT_BUNDLE
2+
3+
## Purpose
4+
Bundle final Workspace V2 session-library/diff/merge/delete/refresh UX correctness fixes without broad refactors.
5+
6+
## Files
7+
- tools/workspace-v2/index.js
8+
- tests/runtime/V2SessionToolsCloseoutBundle.test.mjs
9+
- docs/dev/reports/PR_11_266_session_tools_closeout_bundle_report.md
10+
11+
## Implementation
12+
1. Add diff-output selection key tracking and stale-output clearing.
13+
2. Add explicit diff selection-change handler with current-state status messaging.
14+
3. Add explicit merge selection-change status messaging for ready/blocked states.
15+
4. Ensure refresh and delete actions clear invalid/stale diff output state.
16+
5. Ensure invalid diff payload branch reports both output and global status.
17+
6. Keep all button enablement from the existing computed deterministic model.
18+
19+
## Acceptance
20+
- No stale diff/merge action messaging after selection/delete/refresh changes.
21+
- Invalid action states are clear and safe.
22+
- Merge/diff controls remain model-driven under PR_11_264/265 design.
23+
- No fallback/default data behavior introduced.
24+
25+
## Validation
26+
- node --check tools/workspace-v2/index.js
27+
- node --check tests/runtime/V2SessionToolsCloseoutBundle.test.mjs
28+
- node tests/runtime/V2SessionToolsCloseoutBundle.test.mjs
29+
- node tests/runtime/V2DeterministicStateTransitions.test.mjs
30+
- node tests/runtime/V2SessionStateModelConsolidation.test.mjs
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# PLAN_PR_11_266_WORKSPACE_V2_SESSION_TOOLS_CLOSEOUT_BUNDLE
2+
3+
## Purpose
4+
Close out remaining Workspace V2 session tooling UX correctness issues in one targeted, testable bundle.
5+
6+
## Scope
7+
- tools/workspace-v2/index.js
8+
- tests/runtime/V2SessionToolsCloseoutBundle.test.mjs
9+
- docs/report only
10+
11+
## Goals
12+
- Fix stale status text after session selection and refresh actions.
13+
- Fix stale diff output visibility after selection/delete/refresh changes.
14+
- Keep invalid actions visibly blocked with clear current-state messages.
15+
- Keep merge/diff button state behavior under the PR_11_264/265 deterministic model.
16+
- Improve delete/refresh consistency for diff/merge/session views.
17+
18+
## Out of Scope
19+
- No schema changes
20+
- No samples/games/tool-registry changes
21+
- No cross-tool changes
22+
23+
## Validation
24+
- node --check tools/workspace-v2/index.js
25+
- node --check tests/runtime/V2SessionToolsCloseoutBundle.test.mjs
26+
- node tests/runtime/V2SessionToolsCloseoutBundle.test.mjs
27+
- node tests/runtime/V2DeterministicStateTransitions.test.mjs
28+
- node tests/runtime/V2SessionStateModelConsolidation.test.mjs
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import assert from "node:assert/strict";
2+
import fs from "node:fs";
3+
import path from "node:path";
4+
import { execFileSync } from "node:child_process";
5+
import { fileURLToPath, pathToFileURL } from "node:url";
6+
7+
const __filename = fileURLToPath(import.meta.url);
8+
const __dirname = path.dirname(__filename);
9+
const repoRoot = path.resolve(__dirname, "..", "..");
10+
const jsPath = path.join(repoRoot, "tools", "workspace-v2", "index.js");
11+
const testPath = path.join(repoRoot, "tests", "runtime", "V2SessionToolsCloseoutBundle.test.mjs");
12+
const resultsPath = path.join(repoRoot, "tmp", "v2-session-tools-closeout-results.json");
13+
14+
function checkSyntax(filePath) {
15+
try {
16+
execFileSync(process.execPath, ["--check", filePath], {
17+
cwd: repoRoot,
18+
stdio: ["ignore", "pipe", "pipe"]
19+
});
20+
return { ok: true, error: "" };
21+
} catch (error) {
22+
return { ok: false, error: (error?.stderr || error?.stdout || error?.message || "").toString().trim() };
23+
}
24+
}
25+
26+
function diffSelectionStatus({ count, left, right }) {
27+
if (count < 2) return "Create or reopen at least two Workspace V2 sessions before comparing.";
28+
if (!left && !right) return "Select Session A and Session B to enable Compute Diff.";
29+
if (!left) return "Select Session A to enable Compute Diff.";
30+
if (!right) return "Select Session B to enable Compute Diff.";
31+
if (left === right) return "Choose two different sessions to enable Compute Diff.";
32+
return "Diff selections are valid. Compute Diff is ready.";
33+
}
34+
35+
function mergeSelectionStatus({ count, left, right, canConfirm, canApply, canPreview }) {
36+
if (count < 2) return "Create or reopen at least two Workspace V2 sessions before previewing a merge.";
37+
if (!left && !right) return "Select Session A and Session B to enable Preview Merge.";
38+
if (!left) return "Select Session A to enable Preview Merge.";
39+
if (!right) return "Select Session B to enable Preview Merge.";
40+
if (left === right) return "Choose two different sessions to enable Preview Merge.";
41+
if (canApply) return "Preview confirmed. Apply Merge is enabled.";
42+
if (canConfirm) return "Preview ready. Confirm Preview is enabled.";
43+
if (canPreview) return "Merge selections are valid. Run Preview Merge (Dry Run).";
44+
return "Select two different sessions to enable Preview Merge.";
45+
}
46+
47+
export function run() {
48+
const failures = [];
49+
const jsExists = fs.existsSync(jsPath);
50+
const js = jsExists ? fs.readFileSync(jsPath, "utf8") : "";
51+
const jsSyntax = checkSyntax(jsPath);
52+
const testSyntax = checkSyntax(testPath);
53+
54+
if (!jsExists) failures.push("Missing tools/workspace-v2/index.js.");
55+
if (!jsSyntax.ok) failures.push("tools/workspace-v2/index.js failed syntax check.");
56+
if (!testSyntax.ok) failures.push("tests/runtime/V2SessionToolsCloseoutBundle.test.mjs failed syntax check.");
57+
58+
const requiredTokens = [
59+
"this.diffOutputSelectionKey = \"\";",
60+
"handleDiffSelectionChange()",
61+
"clearDiffOutputForStateChange(statusMessage, outputMessage)",
62+
"this.diffLeftSelect.addEventListener(\"change\", () => {",
63+
"this.handleDiffSelectionChange();",
64+
"Workspace V2 session views refreshed. Compute Diff or Preview Merge for current selections.",
65+
"Selections changed. Compute Diff again.",
66+
"Diff selection is no longer valid. Select Session A and Session B, then compute diff.",
67+
"this.diffOutputSelectionKey = this.buildMergeSelectionKey(left.id, right.id);",
68+
"this.statusNode.textContent = \"Selected diff payload is invalid.\";",
69+
"this.diffOutputSelectionKey.split(\"|\").includes(sessionSelectionId)",
70+
"this.statusNode.textContent = \"Merge selections are valid. Run Preview Merge (Dry Run).\";"
71+
];
72+
requiredTokens.forEach((token) => {
73+
if (!js.includes(token)) failures.push(`Missing PR_11_266 closeout token/text: ${token}`);
74+
});
75+
76+
if (diffSelectionStatus({ count: 0, left: "", right: "" }) !== "Create or reopen at least two Workspace V2 sessions before comparing.") {
77+
failures.push("Diff empty-state status logic mismatch.");
78+
}
79+
if (diffSelectionStatus({ count: 2, left: "", right: "" }) !== "Select Session A and Session B to enable Compute Diff.") {
80+
failures.push("Diff missing-selection status mismatch.");
81+
}
82+
if (diffSelectionStatus({ count: 2, left: "a", right: "a" }) !== "Choose two different sessions to enable Compute Diff.") {
83+
failures.push("Diff same-selection status mismatch.");
84+
}
85+
if (diffSelectionStatus({ count: 2, left: "a", right: "b" }) !== "Diff selections are valid. Compute Diff is ready.") {
86+
failures.push("Diff ready-status mismatch.");
87+
}
88+
89+
if (mergeSelectionStatus({ count: 0, left: "", right: "", canConfirm: false, canApply: false, canPreview: false }) !== "Create or reopen at least two Workspace V2 sessions before previewing a merge.") {
90+
failures.push("Merge empty-state status logic mismatch.");
91+
}
92+
if (mergeSelectionStatus({ count: 2, left: "a", right: "a", canConfirm: false, canApply: false, canPreview: false }) !== "Choose two different sessions to enable Preview Merge.") {
93+
failures.push("Merge same-selection status mismatch.");
94+
}
95+
if (mergeSelectionStatus({ count: 2, left: "a", right: "b", canConfirm: false, canApply: false, canPreview: true }) !== "Merge selections are valid. Run Preview Merge (Dry Run).") {
96+
failures.push("Merge ready-status mismatch.");
97+
}
98+
if (mergeSelectionStatus({ count: 2, left: "a", right: "b", canConfirm: true, canApply: false, canPreview: true }) !== "Preview ready. Confirm Preview is enabled.") {
99+
failures.push("Merge confirm-status mismatch.");
100+
}
101+
if (mergeSelectionStatus({ count: 2, left: "a", right: "b", canConfirm: false, canApply: true, canPreview: true }) !== "Preview confirmed. Apply Merge is enabled.") {
102+
failures.push("Merge apply-status mismatch.");
103+
}
104+
105+
fs.mkdirSync(path.dirname(resultsPath), { recursive: true });
106+
fs.writeFileSync(resultsPath, `${JSON.stringify({
107+
generatedAt: new Date().toISOString(),
108+
failures,
109+
checks: { jsExists, jsSyntax, testSyntax },
110+
scenarios: {
111+
diff: {
112+
empty: diffSelectionStatus({ count: 0, left: "", right: "" }),
113+
missing: diffSelectionStatus({ count: 2, left: "", right: "" }),
114+
same: diffSelectionStatus({ count: 2, left: "a", right: "a" }),
115+
ready: diffSelectionStatus({ count: 2, left: "a", right: "b" })
116+
},
117+
merge: {
118+
empty: mergeSelectionStatus({ count: 0, left: "", right: "", canConfirm: false, canApply: false, canPreview: false }),
119+
same: mergeSelectionStatus({ count: 2, left: "a", right: "a", canConfirm: false, canApply: false, canPreview: false }),
120+
ready: mergeSelectionStatus({ count: 2, left: "a", right: "b", canConfirm: false, canApply: false, canPreview: true }),
121+
confirm: mergeSelectionStatus({ count: 2, left: "a", right: "b", canConfirm: true, canApply: false, canPreview: true }),
122+
apply: mergeSelectionStatus({ count: 2, left: "a", right: "b", canConfirm: false, canApply: true, canPreview: true })
123+
}
124+
}
125+
}, null, 2)}
126+
`, "utf8");
127+
128+
console.log(`v2 session-tools-closeout results: ${resultsPath}`);
129+
assert.equal(failures.length, 0, `V2 session-tools-closeout failures: ${failures.join(" | ")}`);
130+
return { failures };
131+
}
132+
133+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
134+
try {
135+
const summary = run();
136+
console.log(JSON.stringify(summary, null, 2));
137+
} catch (error) {
138+
console.error(error);
139+
process.exitCode = 1;
140+
}
141+
}

0 commit comments

Comments
 (0)