|
| 1 | +# BUILD_PR_LEVEL_20_27_FORCE_DELEGATED_WORKSPACE_PAGER_EVENTS Validation |
| 2 | + |
| 3 | +## Changed files |
| 4 | +- tools/shared/platformShell.js |
| 5 | +- tools/Workspace Manager/main.js |
| 6 | +- docs/dev/reports/workspace_pager_delegated_events_validation.md |
| 7 | + |
| 8 | +## Why previous binding failed |
| 9 | +- `tools/Workspace Manager/main.js:529` binds click handling on `document` in the Workspace Manager host page. |
| 10 | +- Pager controls are rendered by platform shell markup in `tools/shared/platformShell.js:882` and can be recreated during shell re-render (`tools/shared/platformShell.js:1220`). |
| 11 | +- Result: previous direct/host-side binding did not reliably receive live pager clicks from the rendered pager path, so PREV/NEXT appeared non-functional. |
| 12 | + |
| 13 | +## Stable parent delegated listener |
| 14 | +- Stable parent used: `[data-tools-platform-header]` via `queryFirst("[data-tools-platform-header]")` in `tools/shared/platformShell.js:1182`. |
| 15 | +- Delegated click routing is done with: |
| 16 | +- `target.closest("[data-tool-host-prev]")` at `tools/shared/platformShell.js:1194` |
| 17 | +- `target.closest("[data-tool-host-next]")` at `tools/shared/platformShell.js:1195` |
| 18 | +- Action is bridged to Workspace Manager with `window.top.postMessage({ type: "workspace-pager-action", action }, window.location.origin)` at `tools/shared/platformShell.js:1210`. |
| 19 | + |
| 20 | +## Selector and binding proof |
| 21 | +- `[data-tool-host-prev]` and `[data-tool-host-next]` exist in live pager markup at `tools/shared/platformShell.js:882`. |
| 22 | +- Delegated listener one-time guard exists: |
| 23 | +- `let workspacePagerDelegatedBound = false;` at `tools/shared/platformShell.js:23` |
| 24 | +- `if (workspacePagerDelegatedBound) return;` at `tools/shared/platformShell.js:1179` |
| 25 | +- `workspacePagerDelegatedBound = true;` at `tools/shared/platformShell.js:1187` |
| 26 | +- Render re-entry is safe: |
| 27 | +- `renderShell(currentTool)` calls `bindWorkspacePagerDelegatedEvents()` at `tools/shared/platformShell.js:1235` |
| 28 | +- Guard prevents duplicate listeners on repeated render. |
| 29 | + |
| 30 | +## PREV/NEXT fired and remount flow proof |
| 31 | +- Shell-side click diagnostic: |
| 32 | +- `console.info("[WorkspacePager] ... handler fired.")` at `tools/shared/platformShell.js:1202`. |
| 33 | +- Workspace Manager receives delegated action: |
| 34 | +- `window.addEventListener("message", ...)` at `tools/Workspace Manager/main.js:578` |
| 35 | +- `payload.type === "workspace-pager-action"` check at `tools/Workspace Manager/main.js:583` |
| 36 | +- Workspace diagnostic: |
| 37 | +- `console.info("[WorkspaceManager] ... delegated handler fired.")` at `tools/Workspace Manager/main.js:594` |
| 38 | +- NEXT/PREV state update path: |
| 39 | +- `selectToolByOffset(offset)` at `tools/Workspace Manager/main.js:607` |
| 40 | +- `writeSelectedToolId(...)` inside selector at `tools/Workspace Manager/main.js:275` |
| 41 | +- Remount/activate path: |
| 42 | +- `mountSelectedTool(action)` at `tools/Workspace Manager/main.js:617` |
| 43 | +- `runtime.mountTool(toolId, ...)` at `tools/Workspace Manager/main.js:669` |
| 44 | +- Label update after mount: |
| 45 | +- `onMounted(tool) { setCurrentLabel(tool.displayName); }` at `tools/Workspace Manager/main.js:627` |
| 46 | + |
| 47 | +## Missing state/tool list diagnostic proof |
| 48 | +- Missing tool list check: `if (toolIds.length === 0)` at `tools/Workspace Manager/main.js:596` |
| 49 | +- Visible diagnostic on failure: `renderMountDiagnostic(...)` at `tools/Workspace Manager/main.js:598` |
| 50 | +- Selection failure diagnostic: `renderMountDiagnostic(...)` at `tools/Workspace Manager/main.js:609` |
| 51 | +- Silent no-op is not used in these failure paths. |
| 52 | + |
| 53 | +## No button-text dependency proof |
| 54 | +- Click path uses data attributes only (`closest("[data-tool-host-prev]")` / `closest("[data-tool-host-next]")`). |
| 55 | +- No logic reads `[PREV]`/`[NEXT]` text as state. |
| 56 | + |
| 57 | +## Game launch without `tool=` proof |
| 58 | +- `readRequestedToolIdFromQuery()` exists at `tools/Workspace Manager/main.js:313`. |
| 59 | +- Missing/invalid `tool` resolves to empty at `tools/Workspace Manager/main.js:315`. |
| 60 | +- Initialization does not require `tool=`: |
| 61 | +- `const initialToolId = requestedToolId || (toolIds[0] || "");` at `tools/Workspace Manager/main.js:857`. |
| 62 | + |
| 63 | +## `gameId || game` fallback not restored |
| 64 | +- Explicit game query remains `gameId`: |
| 65 | +- `const gameId = (url.searchParams.get("gameId") || "").trim();` at `tools/Workspace Manager/main.js:329`. |
| 66 | +- Static search confirmation: |
| 67 | +- `NOT_FOUND url.searchParams.get("game")` |
| 68 | +- `NOT_FOUND gameId || game` |
| 69 | + |
| 70 | +## Samples remain untouched |
| 71 | +- `git diff --name-only -- samples games` returned no changed files. |
| 72 | +- Existing labels remain: |
| 73 | +- `Open <tool>` in `samples/index.render.js:104` |
| 74 | +- `Open with Workspace Manager` in `games/index.render.js:263` |
| 75 | + |
| 76 | +## Anti-pattern self-check |
| 77 | +- No stale DOM text parsing for pager actions. |
| 78 | +- No button-text-driven click logic. |
| 79 | +- No duplicate delegated listener on repeated render (guarded one-time bind). |
| 80 | +- No new duplicate pager introduced. |
| 81 | +- No `gameId || game` fallback restored. |
| 82 | +- No samples changes. |
| 83 | +- No broad refactor or scope expansion outside pager event routing and diagnostics. |
0 commit comments