Skip to content

Commit 9171787

Browse files
author
DavidQ
committed
Log and fix Preview Generator repoPath hydration from workspace launch context - PR_26127_018-preview-repopath-debug-and-enable-fix
1 parent b90c8f8 commit 9171787

3 files changed

Lines changed: 130 additions & 11 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# PR_26127_018-preview-repopath-debug-and-enable-fix
2+
3+
## Summary
4+
- Preview Generator V2 now logs raw Workspace Manager V2 launch path fields during workspace launch hydration.
5+
- Repo path resolution now considers the actual launch payload and nested manifest object before falling back to display-only fields.
6+
- Generate Preview remains enabled when the resolved repoPath is absolute and the manifest preview target is valid.
7+
8+
## RepoPath Debug Notes
9+
- Status logs now include:
10+
- `launchContext.repoPath`
11+
- `launchContext.manifest.repoPath`
12+
- `launchContext.repoRoot`
13+
- `manifest.repoPath`
14+
- `manifest.repoRoot`
15+
- Status logs now include the selected repoPath source field, resolved value, and absolute-path decision.
16+
- If repoPath is missing, Status logs the exact repoPath fields checked and missing.
17+
- No `/__workspace-manager-v2/repo-root` lookup is used.
18+
19+
## Hydration Notes
20+
- Workspace launch context accepts either the manifest directly or a payload containing `manifest`.
21+
- Absolute repoPath values from the launch payload/manifest are used for direct preview write path resolution.
22+
- Display-only repoRoot still allows Preview Generator V2 to open, but keeps Generate Preview disabled until an absolute repoPath is available.
23+
- Pick Repo Folder stays hidden for Workspace Manager V2 launches.
24+
25+
## Validation
26+
- `npm run test:workspace-v2`
27+
- Result: Pass, 10 tests passed.
28+
29+
## Manual Validation
30+
1. Open Workspace Manager V2 and load Asteroids.
31+
2. Launch Preview Generator V2 from the Preview Generator V2 tile.
32+
3. Confirm Status shows all raw repoPath/repoRoot fields and the resolved repoPath decision.
33+
4. Confirm Repo selected shows the absolute repoPath and Pick Repo Folder is hidden.
34+
5. Confirm Generate Image is enabled when repoPath is absolute and preview target is valid.
35+
6. Confirm Status does not mention `/__workspace-manager-v2/repo-root`.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,14 @@ test.describe("Workspace Manager V2 bootstrap", () => {
545545
await expect(page.locator("#lastGeneratedImageMeta")).toHaveText("Preview target: games/Asteroids/assets/images/preview.png");
546546
const absoluteAsteroidsPreviewPath = path.join(server.repoRoot, "games", "Asteroids", "assets", "images", "preview.svg");
547547
const displayedAsteroidsPreviewPath = `${manifestRepoPath(server)}/games/Asteroids/assets/images/preview.svg`;
548+
await expect(page.locator("#log")).toContainText(`Raw workspace launch path field launchContext.repoPath: ${manifestRepoPath(server)}`);
549+
await expect(page.locator("#log")).toContainText("Raw workspace launch path field launchContext.manifest.repoPath: (missing)");
550+
await expect(page.locator("#log")).toContainText("Raw workspace launch path field launchContext.repoRoot: HTML-JavaScript-Gaming");
551+
await expect(page.locator("#log")).toContainText(`Raw workspace launch path field manifest.repoPath: ${manifestRepoPath(server)}`);
552+
await expect(page.locator("#log")).toContainText("Raw workspace launch path field manifest.repoRoot: HTML-JavaScript-Gaming");
553+
await expect(page.locator("#log")).toContainText("Resolved repoPath decision source field used: launchContext.repoPath");
554+
await expect(page.locator("#log")).toContainText(`Resolved repoPath decision value: ${manifestRepoPath(server)}`);
555+
await expect(page.locator("#log")).toContainText("Resolved repoPath decision absolute: true");
548556
await expect(page.locator("#log")).toContainText("OK Workspace launch context hydrated for Asteroids.");
549557
await expect(page.locator("#log")).toContainText("Workspace repoRoot display label available: HTML-JavaScript-Gaming.");
550558
await expect(page.locator("#log")).toContainText(`Workspace repoPath available: ${manifestRepoPath(server)}.`);
@@ -649,6 +657,15 @@ test.describe("Workspace Manager V2 bootstrap", () => {
649657
await expect(page.locator("#pickRepoBtn")).toBeHidden();
650658
await expect(page.locator("#executeBtn")).toBeDisabled();
651659
await expect(page.locator("#previewTargetValue")).toHaveText("games/Asteroids/assets/images/preview.svg");
660+
await expect(page.locator("#log")).toContainText("Raw workspace launch path field launchContext.repoPath: (missing)");
661+
await expect(page.locator("#log")).toContainText("Raw workspace launch path field launchContext.manifest.repoPath: (missing)");
662+
await expect(page.locator("#log")).toContainText("Raw workspace launch path field launchContext.repoRoot: HTML-JavaScript-Gaming");
663+
await expect(page.locator("#log")).toContainText("Raw workspace launch path field manifest.repoPath: (missing)");
664+
await expect(page.locator("#log")).toContainText("Raw workspace launch path field manifest.repoRoot: HTML-JavaScript-Gaming");
665+
await expect(page.locator("#log")).toContainText("Resolved repoPath decision source field used: (none)");
666+
await expect(page.locator("#log")).toContainText("Resolved repoPath decision value: (missing)");
667+
await expect(page.locator("#log")).toContainText("Resolved repoPath decision absolute: false");
668+
await expect(page.locator("#log")).toContainText("Missing repoPath fields checked: launchContext.repoPath, launchContext.manifest.repoPath, manifest.repoPath");
652669
await expect(page.locator("#log")).toContainText("OK Workspace launch context hydrated for Asteroids.");
653670
await expect(page.locator("#log")).toContainText("Workspace repoRoot display label available: HTML-JavaScript-Gaming.");
654671
await expect(page.locator("#log")).toContainText("Workspace repoPath missing.");

tools/preview-generator-v2/PreviewGeneratorV2App.js

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,57 @@ function repoRootNameMatches(selectedRepoName, expectedRepoRoot) {
140140
return selectedRepoName === expectedFolderName;
141141
}
142142

143+
function isPlainObject(value) {
144+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
145+
}
146+
147+
function isWorkspaceManifest(value) {
148+
return isPlainObject(value) && value.documentKind === "workspace-manifest";
149+
}
150+
151+
function workspaceManifestFromLaunchContext(launchContext) {
152+
if (isWorkspaceManifest(launchContext)) {
153+
return launchContext;
154+
}
155+
if (isWorkspaceManifest(launchContext?.manifest)) {
156+
return launchContext.manifest;
157+
}
158+
return null;
159+
}
160+
161+
function displayRawLaunchFieldValue(value) {
162+
const text = String(value || "").trim();
163+
return text || "(missing)";
164+
}
165+
166+
function launchPathFields(launchContext, manifest) {
167+
return [
168+
{ label: "launchContext.repoPath", value: launchContext?.repoPath },
169+
{ label: "launchContext.manifest.repoPath", value: launchContext?.manifest?.repoPath },
170+
{ label: "launchContext.repoRoot", value: launchContext?.repoRoot },
171+
{ label: "manifest.repoPath", value: manifest?.repoPath },
172+
{ label: "manifest.repoRoot", value: manifest?.repoRoot }
173+
].map((field) => ({
174+
...field,
175+
text: String(field.value || "").trim()
176+
}));
177+
}
178+
179+
function resolveRepoPathDecision(pathFields) {
180+
const repoPathFields = pathFields.filter((field) => field.label.endsWith(".repoPath"));
181+
const firstAbsolute = repoPathFields.find((field) => field.text && isAbsoluteFilesystemPath(field.text));
182+
const firstAvailable = repoPathFields.find((field) => field.text);
183+
const selected = firstAbsolute || firstAvailable || null;
184+
return {
185+
checkedMissingFields: repoPathFields
186+
.filter((field) => !field.text)
187+
.map((field) => field.label),
188+
isAbsolute: Boolean(selected?.text && isAbsoluteFilesystemPath(selected.text)),
189+
sourceField: selected?.label || "(none)",
190+
value: selected?.text || ""
191+
};
192+
}
193+
143194
function isWorkspaceManagerLaunch() {
144195
return runtimeParams.get("launch") === "workspace"
145196
&& runtimeParams.get("fromTool") === "workspace-manager-v2";
@@ -158,17 +209,23 @@ function readWorkspaceLaunchContext() {
158209
return { ok: false, message: "Workspace Manager V2 manifest was not found in sessionStorage." };
159210
}
160211
try {
161-
const manifest = JSON.parse(rawValue);
162-
if (manifest?.documentKind !== "workspace-manifest") {
163-
return { ok: false, message: "Workspace Manager V2 context is not a workspace manifest." };
212+
const launchContext = JSON.parse(rawValue);
213+
const manifest = workspaceManifestFromLaunchContext(launchContext);
214+
if (!manifest) {
215+
return { ok: false, message: "Workspace Manager V2 context is not a workspace manifest or manifest launch payload." };
164216
}
165217
if (!manifest.gameId || !manifest.gameRoot || !manifest.assetsPath) {
166218
return { ok: false, message: "Workspace Manager V2 manifest is missing gameId, gameRoot, or assetsPath." };
167219
}
168-
if (!String(manifest.repoRoot || "").trim() && !String(manifest.repoPath || "").trim()) {
220+
const pathFields = launchPathFields(launchContext, manifest);
221+
const repoPathDecision = resolveRepoPathDecision(pathFields);
222+
const repoRootText = pathFields
223+
.filter((field) => field.label.endsWith(".repoRoot"))
224+
.find((field) => field.text)?.text || "";
225+
if (!repoRootText && !String(repoPathDecision.value || "").trim()) {
169226
return { ok: false, message: "Workspace Manager V2 manifest is missing repoRoot display label and repoPath." };
170227
}
171-
return { ok: true, manifest };
228+
return { ok: true, launchContext, manifest, pathFields, repoPathDecision };
172229
} catch (error) {
173230
return { ok: false, message: `Workspace Manager V2 manifest JSON is invalid: ${error.message}` };
174231
}
@@ -1043,6 +1100,16 @@ class PreviewGeneratorV2App {
10431100
}
10441101

10451102
const manifest = contextResult.manifest;
1103+
contextResult.pathFields.forEach((field) => {
1104+
logger.log(`Raw workspace launch path field ${field.label}: ${displayRawLaunchFieldValue(field.text)}`);
1105+
});
1106+
logger.log(`Resolved repoPath decision source field used: ${contextResult.repoPathDecision.sourceField}`);
1107+
logger.log(`Resolved repoPath decision value: ${displayRawLaunchFieldValue(contextResult.repoPathDecision.value)}`);
1108+
logger.log(`Resolved repoPath decision absolute: ${contextResult.repoPathDecision.isAbsolute ? "true" : "false"}`);
1109+
if (!contextResult.repoPathDecision.value) {
1110+
logger.log(`Missing repoPath fields checked: ${contextResult.repoPathDecision.checkedMissingFields.join(", ")}`);
1111+
}
1112+
10461113
const previewTarget = workspacePreviewTarget(manifest);
10471114
if (!previewTarget.ok) {
10481115
logger.log(`FAIL Workspace launch context hydration: ${previewTarget.message}`);
@@ -1051,20 +1118,20 @@ class PreviewGeneratorV2App {
10511118
}
10521119

10531120
const manifestRepoRoot = String(manifest.repoRoot || "").trim();
1054-
const manifestRepoPath = String(manifest.repoPath || "").trim();
1055-
repoDisplayName = manifestRepoPath || manifestRepoRoot;
1121+
const resolvedRepoPath = contextResult.repoPathDecision.value;
1122+
repoDisplayName = resolvedRepoPath || manifestRepoRoot;
10561123
repoDirHandle = null;
10571124
workspaceLaunchHydrated = true;
10581125
workspaceRepoRootName = manifestRepoRoot;
10591126
workspacePreviewAssetFolder = previewTarget.previewAssetFolder;
10601127
workspacePreviewGameId = manifest.gameId;
10611128
workspaceManifestPreviewPath = previewTarget.manifestPreviewPath;
10621129
workspaceGeneratedPreviewPath = previewTarget.generatedPreviewPath;
1063-
if (manifestRepoPath) {
1130+
if (resolvedRepoPath) {
10641131
try {
1065-
workspaceAbsolutePreviewOutputPath = resolveWorkspaceAbsolutePreviewOutputPath(manifestRepoPath, previewTarget.generatedPreviewPath);
1132+
workspaceAbsolutePreviewOutputPath = resolveWorkspaceAbsolutePreviewOutputPath(resolvedRepoPath, previewTarget.generatedPreviewPath);
10661133
workspaceRepoPathHydrated = true;
1067-
workspaceResolvedRepoPath = manifestRepoPath;
1134+
workspaceResolvedRepoPath = resolvedRepoPath;
10681135
} catch (error) {
10691136
logger.log(`WARN Workspace direct preview write unavailable: ${error.message}`);
10701137
}
@@ -1077,7 +1144,7 @@ class PreviewGeneratorV2App {
10771144
await updatePathPreviewLabels();
10781145
logger.log(`OK Workspace launch context hydrated for ${manifest.gameId}.`);
10791146
logger.log(`Workspace repoRoot display label available: ${manifestRepoRoot || "(empty)"}.`);
1080-
logger.log(`Workspace repoPath ${manifestRepoPath ? `available: ${manifestRepoPath}` : "missing"}.`);
1147+
logger.log(`Workspace repoPath ${resolvedRepoPath ? `available: ${resolvedRepoPath}` : "missing"}.`);
10811148
if (workspaceRepoPathHydrated) {
10821149
logger.log(`Resolved repoPath: ${workspaceResolvedRepoPath}`);
10831150
logger.log(`Resolved absolute preview output path: ${workspaceAbsolutePreviewOutputPath}`);

0 commit comments

Comments
 (0)