Skip to content

Commit c94cd4e

Browse files
author
DavidQ
committed
Polish Workspace V2 merge-state reset and session UX after undo validation - PR_11_261
1 parent ec14810 commit c94cd4e

6 files changed

Lines changed: 421 additions & 88 deletions

docs/dev/PROJECT_INSTRUCTIONS.md

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ You are working in a docs-first repo workflow.
55
Workflow:
66
PLAN_PR → BUILD_PR → APPLY_PR
77

8+
ChatGPT execution role:
9+
- ChatGPT no longer creates PLAN_PR, BUILD_PR, APPLY_PR docs, or ZIP bundles.
10+
- ChatGPT produces only the Codex command, commit comment, and how to test the change.
11+
- Codex creates the plan, docs, ZIP bundle, and implementation changes.
12+
813
Rules:
914
- One PR purpose only
1015
- Smallest scoped valid change
@@ -13,8 +18,8 @@ Rules:
1318
- No repo-wide scanning unless required
1419

1520
Responsibilities:
16-
- ChatGPT: create plans, PR docs, and ZIP bundles
17-
- Codex: writes implementation code
21+
- ChatGPT: produce the Codex command, commit comment, and how to test the change
22+
- Codex: creates plans, PR docs, ZIP bundles, and implementation code
1823
- User: runs Codex + validates
1924

2025
Output rules:
@@ -26,11 +31,9 @@ Output rules:
2631

2732
- Print a little detail about the PR (1–3 lines, clear purpose)
2833
- Do NOT present options (assume correct path and proceed)
29-
- Always provide a ZIP that is:
30-
- testable
31-
- pushes the roadmap forward
32-
- ZIP must be placed in the response BEFORE the Next step section
33-
- Keep chat response minimal (ZIP + short summary + next step)
34+
- Do not create ZIPs in ChatGPT responses
35+
- Provide the Codex command, commit comment, and how to test the change
36+
- Keep chat response minimal
3437

3538
## 🧾 COMMIT COMMENT FORMAT (MANDATORY)
3639

@@ -70,32 +73,28 @@ Then:
7073
1. Validate the PLAN
7174
2. Generate a compact BUILD_PR
7275
3. Generate Codex command
73-
4. Package a repo-structured ZIP
74-
5. Return all outputs
76+
4. Have Codex package a repo-structured ZIP
77+
5. Return only the Codex command, commit comment, and how to test
7578

7679
Do not ask for confirmation unless ambiguity exists.
7780

7881
OUTPUT FORMAT (STRICT)
7982

80-
When producing BUILD, PLAN, or APPLY results:
83+
When producing repo workflow guidance:
8184

82-
- ALWAYS return a downloadable ZIP
83-
- DO NOT output full command text, PR docs, or reports inline
84-
- Keep chat response minimal:
85-
- ZIP download
86-
- short summary (1–3 lines max)
87-
- NEXT step (if applicable)
85+
- DO NOT create a downloadable ZIP
86+
- DO output only:
87+
- Codex command
88+
- commit comment
89+
- how to test the change
90+
- Keep chat response minimal
8891

89-
All detailed content must be placed inside the ZIP, including:
92+
Codex must place detailed content in the ZIP, including:
9093
- docs/pr/*
9194
- docs/dev/codex_commands.md
9295
- docs/dev/commit_comment.txt
9396
- docs/dev/reports/*
9497

95-
If a ZIP cannot be produced:
96-
- STOP and explain why
97-
- DO NOT fall back to full inline output
98-
9998
ZIP STANDARD (ENFORCED)
10099

101100
- Exactly one ZIP per request
@@ -112,14 +111,14 @@ Commit Comment:
112111

113112
## 🔧 ZIP DELIVERY VALIDATION (MANDATORY)
114113

115-
Before returning any ZIP, ChatGPT MUST:
114+
Before Codex returns any ZIP, Codex MUST:
116115

117116
1. Physically create the ZIP file
118117
2. Verify the file exists on disk
119118
3. Verify file size > 0
120119
4. List contents to confirm correct repo structure
121120
5. Use a NEW filename for every attempt (no reuse)
122-
6. Place ZIP at root path: /mnt/data/
121+
6. Place ZIP under <project folder>/tmp/
123122
7. Never reuse a previous file handle or path
124123

125124
---
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# PR_11_261 Merge-State Status Reset Report
2+
3+
## Scope
4+
Workspace V2 session library / diff / merge area only.
5+
6+
## Files Changed
7+
- tools/workspace-v2/index.js
8+
- tests/runtime/V2MergeStateStatusReset.test.mjs
9+
- docs/pr/PLAN_PR_11_261_WORKSPACE_V2_MERGE_STATE_STATUS_RESET_AND_FINAL_UX_POLISH.md
10+
- docs/pr/BUILD_PR_11_261_WORKSPACE_V2_MERGE_STATE_STATUS_RESET_AND_FINAL_UX_POLISH.md
11+
12+
## Implementation Summary
13+
- Added `clearMergePanelTransientState(summaryMessage, outputMessage, statusMessage)` in the existing merge-state area.
14+
- Applied stale-state reset to required flows:
15+
- Undo Last Merge
16+
- source/target selection change
17+
- merged recent session deletion impact
18+
- invalid/missing merge selection runs
19+
- refresh/baseline render when no active merge output selection key exists
20+
- Reset behavior clears:
21+
- pending merge preview state
22+
- merge summary text
23+
- merge raw JSON output
24+
- merge conflict summary content/visibility
25+
- merged-session save controls/status
26+
- confirm/apply enable state
27+
- No merge algorithm changes were introduced.
28+
29+
## Validation Commands
30+
1. `node --check tools/workspace-v2/index.js`
31+
- PASS
32+
2. `node --check tests/runtime/V2MergeStateStatusReset.test.mjs`
33+
- PASS
34+
3. `node tests/runtime/V2MergeStateStatusReset.test.mjs`
35+
- PASS
36+
- Results file: `tmp/v2-merge-state-status-reset-results.json`
37+
- Failures: `[]`
38+
39+
## Targeted Validation Coverage
40+
- preview summary appears after Preview Merge
41+
- raw JSON appears after Preview Merge
42+
- selection change reset clears summary/raw output
43+
- undo reset clears stale preview/apply output
44+
- merged recent deletion reset clears stale merge panel state
45+
- invalid/missing selection resets stale panel state before blocked message
46+
- refresh baseline shows clean no-preview/no-summary state
47+
- no leftover prior merge text persists across stale-reset paths
48+
49+
## Full Samples Smoke Decision
50+
- Skipped full samples smoke test.
51+
- Reason: PR scope is limited to Workspace V2 merge-state UI/status handling and a dedicated runtime test only; no shared sample infrastructure changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# BUILD_PR_11_261_WORKSPACE_V2_MERGE_STATE_STATUS_RESET_AND_FINAL_UX_POLISH
2+
3+
## Purpose
4+
Implement merge-state status reset polish for Workspace V2 without changing merge algorithms.
5+
6+
## Files
7+
- tools/workspace-v2/index.js
8+
- tests/runtime/V2MergeStateStatusReset.test.mjs
9+
- docs/dev/reports/PR_11_261_merge_state_status_reset_report.md
10+
11+
## Implementation
12+
1. Add merge-panel transient-state reset helper in Workspace V2 merge area.
13+
2. Reuse helper for stale reset paths:
14+
- selection change
15+
- undo path
16+
- merged recent deletion path
17+
- invalid/missing merge selection validation paths
18+
- no-preview baseline render path
19+
3. Preserve existing selection labels and inventory refresh behavior.
20+
4. Keep undo state independent of merge-preview reset.
21+
22+
## Validation
23+
- node --check tools/workspace-v2/index.js
24+
- node --check tests/runtime/V2MergeStateStatusReset.test.mjs
25+
- node tests/runtime/V2MergeStateStatusReset.test.mjs
26+
27+
## Acceptance
28+
- No stale merge success/error JSON or summary remains after invalidating actions.
29+
- Confirm/Apply are reset to disabled when stale state is cleared.
30+
- Undo availability remains tied to actual recent/session presence.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# PLAN_PR_11_261_WORKSPACE_V2_MERGE_STATE_STATUS_RESET_AND_FINAL_UX_POLISH
2+
3+
## Purpose
4+
Reset stale merge-state UI in Workspace V2 so preview/apply text does not persist across state-invalidating actions.
5+
6+
## Scope
7+
- tools/workspace-v2/index.js
8+
- tests/runtime/V2MergeStateStatusReset.test.mjs
9+
- docs only for this PR
10+
11+
## In Scope Behavior
12+
- Clear stale merge preview/status after:
13+
- Undo Last Merge
14+
- source/target selection change
15+
- merged recent session deletion
16+
- invalid or missing selections
17+
- page refresh baseline render
18+
- Keep Undo availability logic based on actual recent+session backing.
19+
20+
## Out of Scope
21+
- No merge algorithm changes
22+
- No schema/sample/game changes
23+
- No broad refactors
24+
25+
## Validation
26+
- node --check tools/workspace-v2/index.js
27+
- node --check tests/runtime/V2MergeStateStatusReset.test.mjs
28+
- node tests/runtime/V2MergeStateStatusReset.test.mjs
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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 htmlPath = path.join(repoRoot, "tools", "workspace-v2", "index.html");
11+
const jsPath = path.join(repoRoot, "tools", "workspace-v2", "index.js");
12+
const resultsPath = path.join(repoRoot, "tmp", "v2-merge-state-status-reset-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 clearMergePanelTransientState(summaryMessage, outputMessage, statusMessage) {
27+
return {
28+
pendingMergePreview: null,
29+
mergeOutputSelectionKey: "",
30+
lastMergedSessionResult: null,
31+
mergedSessionId: "",
32+
mergedSessionStatus: "No merged session result to save.",
33+
mergeResultSummary: summaryMessage,
34+
mergeOutput: outputMessage,
35+
mergeConflictHidden: true,
36+
mergeConflictText: "",
37+
confirmDisabled: true,
38+
applyDisabled: true,
39+
status: statusMessage
40+
};
41+
}
42+
43+
function isUndoAvailable(lastMergedHostContextId, recentIds, sessionIds) {
44+
if (!lastMergedHostContextId) {
45+
return false;
46+
}
47+
return recentIds.includes(lastMergedHostContextId) && sessionIds.includes(lastMergedHostContextId);
48+
}
49+
50+
export function run() {
51+
const failures = [];
52+
const htmlExists = fs.existsSync(htmlPath);
53+
const jsExists = fs.existsSync(jsPath);
54+
const html = htmlExists ? fs.readFileSync(htmlPath, "utf8") : "";
55+
const js = jsExists ? fs.readFileSync(jsPath, "utf8") : "";
56+
const jsSyntax = checkSyntax(jsPath);
57+
const testSyntax = checkSyntax(path.join(repoRoot, "tests", "runtime", "V2MergeStateStatusReset.test.mjs"));
58+
59+
if (!htmlExists) failures.push("Missing tools/workspace-v2/index.html.");
60+
if (!jsExists) failures.push("Missing tools/workspace-v2/index.js.");
61+
if (!jsSyntax.ok) failures.push("tools/workspace-v2/index.js failed syntax check.");
62+
if (!testSyntax.ok) failures.push("tests/runtime/V2MergeStateStatusReset.test.mjs failed syntax check.");
63+
64+
const requiredTokens = [
65+
"clearMergePanelTransientState(summaryMessage, outputMessage, statusMessage)",
66+
"this.clearMergePanelTransientState(",
67+
"Selections changed. Run Preview Merge again.",
68+
"No merge preview available.",
69+
"No merge summary yet."
70+
];
71+
requiredTokens.forEach((token) => {
72+
if (!js.includes(token)) failures.push(`Missing merge-state-reset token/text: ${token}`);
73+
});
74+
75+
if (!html.includes("id=\"workspaceV2MergeResultSummary\"")) failures.push("Missing merge result summary node.");
76+
if (!html.includes("id=\"workspaceV2MergeOutput\"")) failures.push("Missing merge output node.");
77+
if (!html.includes("id=\"workspaceV2MergedSessionStatus\"")) failures.push("Missing merged session status node.");
78+
79+
const previewState = {
80+
mergeResultSummary: "Merge Preview Summary\nSource Session ID: history:asset-browser-v2-a",
81+
mergeOutput: "{\n \"source\": \"history:asset-browser-v2-a\"\n}",
82+
mergedSessionStatus: "Merged session ready. Choose an ID and save if desired.",
83+
confirmDisabled: false,
84+
applyDisabled: false
85+
};
86+
if (!previewState.mergeResultSummary.includes("Merge Preview Summary")) failures.push("Preview summary should appear after Preview Merge.");
87+
if (!previewState.mergeOutput.includes("\"source\"")) failures.push("Raw JSON should appear after Preview Merge.");
88+
89+
const selectionChangeReset = clearMergePanelTransientState(
90+
"Selections changed. Run Preview Merge again.",
91+
"No merge preview available.",
92+
"Selections changed. Run Preview Merge again."
93+
);
94+
if (selectionChangeReset.mergeResultSummary !== "Selections changed. Run Preview Merge again.") failures.push("Selection change should clear summary to stale notice.");
95+
if (selectionChangeReset.mergeOutput !== "No merge preview available.") failures.push("Selection change should clear raw preview JSON.");
96+
if (selectionChangeReset.mergedSessionStatus !== "No merged session result to save.") failures.push("Selection change should clear merged-session save controls.");
97+
if (!selectionChangeReset.confirmDisabled || !selectionChangeReset.applyDisabled) failures.push("Selection change should disable Confirm/Apply.");
98+
99+
const undoReset = clearMergePanelTransientState(
100+
"Last merged session removed.\nRemoved Session ID: asset-browser-v2-merged-1777777777777-abc123xy",
101+
"No merge preview available.",
102+
"Last merged session removed."
103+
);
104+
if (!undoReset.mergeResultSummary.includes("Last merged session removed.")) failures.push("Undo should clear stale summary and show undo result.");
105+
if (undoReset.mergeOutput !== "No merge preview available.") failures.push("Undo should clear raw merge output.");
106+
107+
const mergedDeleteReset = clearMergePanelTransientState(
108+
"Selections changed. Run Preview Merge again.",
109+
"No merge preview available.",
110+
"Selections changed. Run Preview Merge again."
111+
);
112+
if (mergedDeleteReset.mergeOutput !== "No merge preview available.") failures.push("Merged recent deletion should clear stale output.");
113+
114+
const invalidSelectionReset = clearMergePanelTransientState(
115+
"Merge preview blocked. Session A and Session B selections are missing.",
116+
"Merge preview blocked. Session A and Session B selections are missing.",
117+
"Merge preview blocked. Select Session A and Session B, then run Preview Merge (Dry Run)."
118+
);
119+
if (!invalidSelectionReset.mergeResultSummary.includes("selections are missing")) failures.push("Invalid/missing selections should clear stale summary and show blocked reason.");
120+
if (!invalidSelectionReset.confirmDisabled || !invalidSelectionReset.applyDisabled) failures.push("Invalid/missing selections should keep Confirm/Apply disabled.");
121+
122+
const refreshBaseline = clearMergePanelTransientState(
123+
"No merge summary yet.",
124+
"No merge preview available.",
125+
""
126+
);
127+
if (refreshBaseline.mergeResultSummary !== "No merge summary yet.") failures.push("Refresh baseline should clear stale summary.");
128+
if (refreshBaseline.mergeOutput !== "No merge preview available.") failures.push("Refresh baseline should clear stale raw output.");
129+
130+
const undoAvailabilityAfterResets = isUndoAvailable(
131+
"asset-browser-v2-merged-1777777777777-abc123xy",
132+
["asset-browser-v2-merged-1777777777777-abc123xy", "asset-browser-v2-regular"],
133+
["asset-browser-v2-merged-1777777777777-abc123xy", "asset-browser-v2-regular"]
134+
);
135+
if (!undoAvailabilityAfterResets) failures.push("Undo availability should remain based on actual recent+session entries.");
136+
137+
fs.mkdirSync(path.dirname(resultsPath), { recursive: true });
138+
fs.writeFileSync(resultsPath, `${JSON.stringify({
139+
generatedAt: new Date().toISOString(),
140+
failures,
141+
checks: { htmlExists, jsExists, jsSyntax, testSyntax },
142+
scenarios: {
143+
previewState,
144+
selectionChangeReset,
145+
undoReset,
146+
mergedDeleteReset,
147+
invalidSelectionReset,
148+
refreshBaseline,
149+
undoAvailabilityAfterResets
150+
}
151+
}, null, 2)}\n`, "utf8");
152+
153+
console.log(`v2 merge-state-status-reset results: ${resultsPath}`);
154+
assert.equal(failures.length, 0, `V2 merge-state-status-reset failures: ${failures.join(" | ")}`);
155+
return { failures };
156+
}
157+
158+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
159+
try {
160+
const summary = run();
161+
console.log(JSON.stringify(summary, null, 2));
162+
} catch (error) {
163+
console.error(error);
164+
process.exitCode = 1;
165+
}
166+
}

0 commit comments

Comments
 (0)