diff --git a/docs_build/dev/ProjectInstructions/addendums/table_first_ui.md b/docs_build/dev/ProjectInstructions/addendums/table_first_ui.md index 23ceb1622..5645ebcbb 100644 --- a/docs_build/dev/ProjectInstructions/addendums/table_first_ui.md +++ b/docs_build/dev/ProjectInstructions/addendums/table_first_ui.md @@ -15,3 +15,69 @@ Avoid: Reference implementation: Idea Board is the reference implementation. + + +DB base +Creator Table 1 +Parent Table *-1 user +Child Table -- *-1 Parent + + +No selected Items +┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Idea Board │ +├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ +│ Idea │ Pitch │ Status │ Updated │ Notes │ Actions │ +├───────────────┼─────────────────────────────────────────┼───────────┼────────────┼─────────┼─────────┤ +│ Top Thougts │ Smartest person wins... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ +├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ +│ Sky Orchard │ Grow floating islands... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ +├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ +│ Clockwork... │ Deliver messages through looping city...│ New │ 2026-06-20 │ 0 Notes │ Edit Del│ +├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ +│ [ Add Idea ] │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +Clicking {Sky Orchard {chevron down arrow}] Expands/Acording the Note(s) for that Idea +┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Idea Board │ +├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ +│ Idea │ Pitch │ Status │ Updated │ Notes │ Actions │ +├───────────────┼─────────────────────────────────────────┼───────────┼────────────┼─────────┼─────────┤ +│ Top Thougts │ Smartest person wins... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ +├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ +│ Sky Orchard[^}│ Grow floating islands... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ +├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ +│ Notes │ +│ --------------------------------------------------------------------------------------------------- │ +│ note 1 [Edit] [Delete] │ +│ System seed note: compare early ideas before project creation. [Edit] │ +│ Ask whether the core loop is planning, defense, or both. [Edit] [Delete] │ +│ │ +│ [ Add Note ] │ +├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ +│ Clockwork... │ Deliver messages through looping city...│ New │ 2026-06-20 │ 0 Notes │ Edit Del│ +├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ +│ [ Add Idea ] │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Clickin Add Idea +┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Idea Board │ +├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ +│ Idea │ Pitch │ Status │ Updated │ Notes │ Actions │ +├───────────────┼─────────────────────────────────────────┼───────────┼────────────┼─────────┼─────────┤ +│ Top Thougts │ Smartest person wins... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ +├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ +│ Sky Orchard │ Grow floating islands... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ +├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ +│ Clockwork... │ Deliver messages through looping city...│ New │ 2026-06-20 │ 0 Notes │ Edit Del│ +├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ +│ [input.....] │ [input.....] │ [Dropdown]│ [autofile] │ 0 Notes │ Save Can│ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + + + diff --git a/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-branch-validation.txt b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-branch-validation.txt new file mode 100644 index 000000000..b9a5f29ee --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-branch-validation.txt @@ -0,0 +1,7 @@ +Branch Validation: PASS + +PASS - Current branch is pr/26174-ALFA-013-game-hub-game-row-child-rows. +PASS - Expected branch is pr/26174-ALFA-013-game-hub-game-row-child-rows. +PASS - Branch remains stacked on PR_26174_ALFA_012. +PASS - Owner-updated docs_build/dev/ProjectInstructions/addendums/table_first_ui.md is committed on this branch and used as the parent/child table reference. +PASS - Work stayed scoped to Game Hub row rendering, targeted Playwright expectations, and required reports. diff --git a/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-manual-validation-notes.txt b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-manual-validation-notes.txt new file mode 100644 index 000000000..95c98cf15 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-manual-validation-notes.txt @@ -0,0 +1,10 @@ +Manual Validation Notes: PASS + +- Reviewed docs_build/dev/ProjectInstructions/addendums/table_first_ui.md as the owner-updated table-first reference. +- Compared Game Hub against the Idea Board parent row plus expanded child row pattern. +- Confirmed Game Hub no longer renders standalone Source Idea, Game Foundation, or Readiness Output panel/card sections. +- Confirmed Game Hub no longer uses an Open Games wrapper/accordion or Open Games table wrapper. +- Confirmed expanding a game creates exactly two child rows: Source Idea, then Readiness Output. +- Confirmed Source Idea remains read-only and the readiness rows use existing service-contract output. +- Confirmed safe empty and unavailable state checks still pass. +- Confirmed Idea Board-created Source Idea context still appears in Game Hub through the child table after reload. diff --git a/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-requirement-checklist.txt b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-requirement-checklist.txt new file mode 100644 index 000000000..7d6926eab --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-requirement-checklist.txt @@ -0,0 +1,16 @@ +Requirement Checklist: PASS + +PASS - table_first_ui.md was used as the parent/child table reference. +PASS - Game row is the parent row. +PASS - Source Idea is a child row/table under the game row. +PASS - Readiness Output is a child row/table under the game row. +PASS - Source Idea and Readiness Output are no longer separate card/panel sections. +PASS - Open Games wrapper/accordion is not used. +PASS - Open Games wrapper/table identity is not used. +PASS - Game rows expand/collapse. +PASS - Expanded game rows show Source Idea first and Readiness Output second. +PASS - No new readiness math was added. +PASS - Existing API/service contract was preserved. +PASS - Safe empty/unavailable states were preserved by targeted validation. +PASS - No browser-owned project data was introduced. +PASS - No silent fallbacks were introduced. diff --git a/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-validation-lane.txt b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-validation-lane.txt new file mode 100644 index 000000000..7749e1ad7 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-validation-lane.txt @@ -0,0 +1,11 @@ +Validation Lane: PASS + +Commands: +1. npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs -g "Game Hub creates, opens, and deletes mock games|Game Hub validates game parent rows and child tables|Game Hub shows a creator-safe empty state|Game Hub shows a creator-safe unavailable state|Game Hub readiness child rows update from mock game state" +Result: 5 passed. + +2. npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -g "Idea Board uses accordion table ideas and notes" +Result: 1 passed. + +3. git diff --check -- toolbox/game-hub/index.html toolbox/game-hub/game-hub.js tests/playwright/tools/GameHubMockRepository.spec.mjs tests/playwright/tools/IdeaBoardTableNotes.spec.mjs docs_build/dev/reports/codex_review.diff docs_build/dev/reports/codex_changed_files.txt docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows.md docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-branch-validation.txt docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-requirement-checklist.txt docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-validation-lane.txt docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-manual-validation-notes.txt +Result: PASS. diff --git a/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows.md b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows.md new file mode 100644 index 000000000..cece3f35b --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows.md @@ -0,0 +1,20 @@ +# PR_26174_ALFA_013-game-hub-game-row-child-rows + +## Purpose + +Correct Game Hub so each game is the parent row and Source Idea plus Readiness Output render only as child rows/child tables under that game row. + +## Summary + +- Applied the updated table_first_ui.md guidance and matched the Idea Board parent-row/expanded-child-row pattern. +- Removed the Open Games wrapper/accordion from the Game Hub page. +- Removed the Open Games wrapper/table identity from the Game Hub list table. +- Removed standalone Source Idea, Game Foundation, and Readiness Output panel/card sections. +- Kept Source Idea and Readiness Output as the two expanded child rows under each game parent row. +- Preserved the existing Game Hub API/service contract and safe empty/unavailable states. + +## Validation + +PASS - `npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs -g "Game Hub creates, opens, and deletes mock games|Game Hub validates game parent rows and child tables|Game Hub shows a creator-safe empty state|Game Hub shows a creator-safe unavailable state|Game Hub readiness child rows update from mock game state"` +PASS - `npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -g "Idea Board uses accordion table ideas and notes"` +PASS - `git diff --check -- toolbox/game-hub/index.html toolbox/game-hub/game-hub.js tests/playwright/tools/GameHubMockRepository.spec.mjs tests/playwright/tools/IdeaBoardTableNotes.spec.mjs docs_build/dev/reports/codex_review.diff docs_build/dev/reports/codex_changed_files.txt docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows.md docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-branch-validation.txt docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-requirement-checklist.txt docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-validation-lane.txt docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-manual-validation-notes.txt` diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index 3c4039b1f..34bd72f5e 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,8 +1,12 @@ +docs_build/dev/ProjectInstructions/addendums/table_first_ui.md +toolbox/game-hub/index.html +toolbox/game-hub/game-hub.js tests/playwright/tools/GameHubMockRepository.spec.mjs +tests/playwright/tools/IdeaBoardTableNotes.spec.mjs docs_build/dev/reports/codex_review.diff docs_build/dev/reports/codex_changed_files.txt -docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation.md -docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-branch-validation.txt -docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-requirement-checklist.txt -docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-validation-lane.txt -docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-manual-validation-notes.txt +docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows.md +docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-branch-validation.txt +docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-requirement-checklist.txt +docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-validation-lane.txt +docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-manual-validation-notes.txt diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 41e24b645..714f931d2 100644 --- a/docs_build/dev/reports/codex_review.diff +++ b/docs_build/dev/reports/codex_review.diff @@ -1,135 +1,539 @@ +diff --git a/docs_build/dev/ProjectInstructions/addendums/table_first_ui.md b/docs_build/dev/ProjectInstructions/addendums/table_first_ui.md +index 23ceb1622..5645ebcbb 100644 +--- a/docs_build/dev/ProjectInstructions/addendums/table_first_ui.md ++++ b/docs_build/dev/ProjectInstructions/addendums/table_first_ui.md +@@ -15,3 +15,69 @@ Avoid: + + Reference implementation: + Idea Board is the reference implementation. ++ ++ ++DB base ++Creator Table 1 ++Parent Table *-1 user ++Child Table -- *-1 Parent ++ ++ ++No selected Items ++┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ ++│ Idea Board │ ++├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ ++│ Idea │ Pitch │ Status │ Updated │ Notes │ Actions │ ++├───────────────┼─────────────────────────────────────────┼───────────┼────────────┼─────────┼─────────┤ ++│ Top Thougts │ Smartest person wins... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ ++├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ ++│ Sky Orchard │ Grow floating islands... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ ++├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ ++│ Clockwork... │ Deliver messages through looping city...│ New │ 2026-06-20 │ 0 Notes │ Edit Del│ ++├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ ++│ [ Add Idea ] │ ++└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ ++ ++ ++Clicking {Sky Orchard {chevron down arrow}] Expands/Acording the Note(s) for that Idea ++┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ ++│ Idea Board │ ++├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ ++│ Idea │ Pitch │ Status │ Updated │ Notes │ Actions │ ++├───────────────┼─────────────────────────────────────────┼───────────┼────────────┼─────────┼─────────┤ ++│ Top Thougts │ Smartest person wins... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ ++├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ ++│ Sky Orchard[^}│ Grow floating islands... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ ++├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ ++│ Notes │ ++│ --------------------------------------------------------------------------------------------------- │ ++│ note 1 [Edit] [Delete] │ ++│ System seed note: compare early ideas before project creation. [Edit] │ ++│ Ask whether the core loop is planning, defense, or both. [Edit] [Delete] │ ++│ │ ++│ [ Add Note ] │ ++├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ ++│ Clockwork... │ Deliver messages through looping city...│ New │ 2026-06-20 │ 0 Notes │ Edit Del│ ++├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ ++│ [ Add Idea ] │ ++└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ ++ ++Clickin Add Idea ++┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ ++│ Idea Board │ ++├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ ++│ Idea │ Pitch │ Status │ Updated │ Notes │ Actions │ ++├───────────────┼─────────────────────────────────────────┼───────────┼────────────┼─────────┼─────────┤ ++│ Top Thougts │ Smartest person wins... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ ++├───────────────┬─────────────────────────────────────────┬───────────┬────────────┬─────────┬─────────┤ ++│ Sky Orchard │ Grow floating islands... │ Exploring │ 2026-06-20 │ 3 Notes │ Edit Del│ ++├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ ++│ Clockwork... │ Deliver messages through looping city...│ New │ 2026-06-20 │ 0 Notes │ Edit Del│ ++├───────────────┴─────────────────────────────────────────┴───────────┴────────────┴─────────┴─────────┤ ++│ [input.....] │ [input.....] │ [Dropdown]│ [autofile] │ 0 Notes │ Save Can│ ++└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ ++ ++ ++ ++ ++ diff --git a/tests/playwright/tools/GameHubMockRepository.spec.mjs b/tests/playwright/tools/GameHubMockRepository.spec.mjs -index dac65c676..4e617ed35 100644 +index 4e617ed35..ae1de99c4 100644 --- a/tests/playwright/tools/GameHubMockRepository.spec.mjs +++ b/tests/playwright/tools/GameHubMockRepository.spec.mjs -@@ -335,6 +335,131 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { +@@ -256,7 +256,9 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { + await expect(page.locator("[data-project-record-status]")).toHaveText("Project Information loaded."); + await expect(page.locator("[data-game-project-information]")).toContainText("Project Information"); + await expect(page.locator("[data-project-records-table]")).toContainText("Demo Game"); +- await expect(page.locator("[data-source-idea-section]")).toContainText("No source idea yet"); ++ await expect(page.locator("[data-source-idea-section]")).toHaveCount(0); ++ await expect(page.locator("[data-game-output-panels]")).toHaveCount(0); ++ await expect(page.locator("[data-game-hub-foundation]")).toHaveCount(0); + await expect(page.locator("[data-active-game-name]")).toHaveText("Demo Game"); + await expect(page.locator("[data-active-game-purpose]")).toHaveText("Game"); + await expect(page.locator("[data-current-user-role]")).toHaveText("Owner"); +@@ -264,9 +266,11 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { + await expect(page.locator("[data-game-list]")).toContainText("Gravity Demo"); + await expect(page.locator("[data-game-list]")).toContainText("Collision Demo"); + await expect(page.locator("[data-game-list]")).toContainText("Camera Follow Demo"); +- await expect(page.locator("[data-game-parent-table='open-games']")).toHaveAttribute("aria-label", "Open Games"); +- await expect(page.locator("[data-game-parent-table='open-games'] caption")).toHaveText("Open Games"); +- await expect(page.locator("[data-game-parent-table='open-games'] thead th")).toHaveText([ ++ await expect(page.locator("summary").filter({ hasText: /^Open Games$/ })).toHaveCount(0); ++ await expect(page.locator("[data-game-parent-table='open-games']")).toHaveCount(0); ++ await expect(page.locator("[data-game-rows-table='true']")).toHaveAttribute("aria-label", "Games"); ++ await expect(page.locator("[data-game-rows-table='true'] caption")).toHaveCount(0); ++ await expect(page.locator("[data-game-rows-table='true'] thead th")).toHaveText([ + "Game", + "Purpose", + "Status", +@@ -280,17 +284,14 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { + await expect(demoGameRow.getByRole("button", { name: "Open Demo Game (Active)" })).toHaveAttribute("aria-current", "true"); + await demoGameRow.locator("[data-game-toggle='demo-game']").click(); + await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveAttribute("aria-expanded", "true"); +- await expect(page.locator("[data-game-row='demo-game'] + [data-game-expanded-row='demo-game']")).toHaveCount(1); +- await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='summary']")).toContainText("Game Summary"); +- await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='summary'] tbody tr")).toHaveText([ +- "ProjectDemo Game", +- "PurposeGame", +- "StatusUnder Construction", +- "OwnerUser 1", +- ]); +- await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(3); +- await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='source-idea'] caption")).toHaveText("Source Idea"); +- const readinessOutputTable = page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='readiness-output']"); ++ const demoChildRows = page.locator("[data-game-expanded-row='demo-game']"); ++ await expect(demoChildRows).toHaveCount(2); ++ await expect(demoChildRows.nth(0)).toHaveAttribute("data-game-child-row", "source-idea"); ++ await expect(demoChildRows.nth(1)).toHaveAttribute("data-game-child-row", "readiness-output"); ++ await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='summary']")).toHaveCount(0); ++ await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(2); ++ await expect(demoChildRows.nth(0).locator("[data-game-child-table='source-idea'] caption")).toHaveText("Source Idea"); ++ const readinessOutputTable = demoChildRows.nth(1).locator("[data-game-child-table='readiness-output']"); + await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output"); + await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]); + await expect(readinessOutputTable.locator("tbody tr")).toHaveText([ +@@ -335,7 +336,7 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { } }); -+test("Game Hub validates Open Games parent and child tables", async ({ page }) => { -+ const sourceLinkedGame = { -+ id: "lantern-reef", -+ ownerKey: MOCK_DB_KEYS.users.user1, -+ name: "Lantern Reef", -+ purpose: "Game", -+ status: "Ready for Testing", -+ ownerDisplayName: "User 1", -+ members: [ -+ { -+ displayName: "User 1", -+ gameId: "lantern-reef", -+ permission: "Owner", -+ role: "Owner", -+ userKey: MOCK_DB_KEYS.users.user1, -+ }, -+ ], -+ sourceIdea: { -+ idea: "Lantern Reef", -+ pitch: "Guide reef keepers through dusk storms.", -+ notes: [ -+ "Keep traversal gentle.", -+ "Use warm lantern art.", -+ ], -+ }, -+ }; -+ const journeyBuckets = [ -+ "Idea", -+ "Design", -+ "Graphics", -+ "Audio", -+ "Objects", -+ "Worlds", -+ "Interface", -+ "Controls", -+ "Rules", -+ "Progression", -+ "Play Test", -+ "Publish", -+ "Share", -+ ]; -+ -+ await page.route("**/api/toolbox/game-hub/repositories/*/methods/getActiveGame", async (route) => { -+ await route.fulfill({ -+ body: JSON.stringify({ -+ data: { result: sourceLinkedGame }, -+ ok: true, -+ rule: "Browser -> Server API -> Data Source", -+ }), -+ contentType: "application/json; charset=utf-8", -+ status: 200, -+ }); -+ }); -+ await page.route("**/api/toolbox/game-hub/repositories/*/methods/getGameProgress", async (route) => { -+ await route.fulfill({ -+ body: JSON.stringify({ -+ data: { -+ result: { -+ currentFocus: "Review source idea context", -+ gameProgress: "Lantern Reef identity ready", -+ gameStatus: "Ready for Testing", -+ publishingProgress: "Launch review pending", -+ recommendedNextTool: "Game Journey", -+ progressChecklist: journeyBuckets.map((label) => ({ label, status: "Planned" })), -+ }, -+ }, -+ ok: true, -+ rule: "Browser -> Server API -> Data Source", -+ }), -+ contentType: "application/json; charset=utf-8", -+ status: 200, -+ }); -+ }); -+ await page.route("**/api/toolbox/game-hub/repositories/*/methods/listGames", async (route) => { -+ await route.fulfill({ -+ body: JSON.stringify({ -+ data: { result: [sourceLinkedGame] }, -+ ok: true, -+ rule: "Browser -> Server API -> Data Source", -+ }), -+ contentType: "application/json; charset=utf-8", -+ status: 200, -+ }); -+ }); -+ const failures = await openRepoPage(page, "/toolbox/game-hub/index.html", { session: creatorSession() }); -+ -+ try { -+ await expect(page.locator("[data-game-parent-table='open-games'] caption")).toHaveText("Open Games"); -+ const parentRows = page.locator("[data-game-parent-table='open-games'] tbody > [data-game-row]"); -+ await expect(parentRows).toHaveCount(1); -+ const gameRow = page.locator("[data-game-row='lantern-reef']"); -+ await expect(gameRow).toContainText("Lantern Reef"); -+ await expect(gameRow.locator("[data-game-toggle='lantern-reef']")).toHaveAttribute("aria-expanded", "false"); +-test("Game Hub validates Open Games parent and child tables", async ({ page }) => { ++test("Game Hub validates game parent rows and child tables", async ({ page }) => { + const sourceLinkedGame = { + id: "lantern-reef", + ownerKey: MOCK_DB_KEYS.users.user1, +@@ -422,8 +423,11 @@ test("Game Hub validates Open Games parent and child tables", async ({ page }) = + const failures = await openRepoPage(page, "/toolbox/game-hub/index.html", { session: creatorSession() }); + + try { +- await expect(page.locator("[data-game-parent-table='open-games'] caption")).toHaveText("Open Games"); +- const parentRows = page.locator("[data-game-parent-table='open-games'] tbody > [data-game-row]"); ++ await expect(page.locator("summary").filter({ hasText: /^Open Games$/ })).toHaveCount(0); ++ await expect(page.locator("[data-game-parent-table='open-games']")).toHaveCount(0); ++ await expect(page.locator("[data-game-rows-table='true']")).toHaveAttribute("aria-label", "Games"); ++ await expect(page.locator("[data-game-rows-table='true'] caption")).toHaveCount(0); ++ const parentRows = page.locator("[data-game-rows-table='true'] tbody > [data-game-row]"); + await expect(parentRows).toHaveCount(1); + const gameRow = page.locator("[data-game-row='lantern-reef']"); + await expect(gameRow).toContainText("Lantern Reef"); +@@ -431,11 +435,14 @@ test("Game Hub validates Open Games parent and child tables", async ({ page }) = + + await gameRow.locator("[data-game-toggle='lantern-reef']").click(); + await expect(gameRow.locator("[data-game-toggle='lantern-reef']")).toHaveAttribute("aria-expanded", "true"); +- const expandedRow = page.locator("[data-game-expanded-row='lantern-reef']"); +- await expect(expandedRow).toHaveCount(1); +- await expect(expandedRow.locator("[data-game-child-table]")).toHaveCount(3); +- +- const sourceIdeaTable = expandedRow.locator("[data-game-child-table='source-idea']"); ++ const expandedRows = page.locator("[data-game-expanded-row='lantern-reef']"); ++ await expect(expandedRows).toHaveCount(2); ++ await expect(expandedRows.nth(0)).toHaveAttribute("data-game-child-row", "source-idea"); ++ await expect(expandedRows.nth(1)).toHaveAttribute("data-game-child-row", "readiness-output"); ++ await expect(expandedRows.locator("[data-game-child-table]")).toHaveCount(2); ++ await expect(expandedRows.locator("[data-game-child-table='summary']")).toHaveCount(0); + -+ await gameRow.locator("[data-game-toggle='lantern-reef']").click(); -+ await expect(gameRow.locator("[data-game-toggle='lantern-reef']")).toHaveAttribute("aria-expanded", "true"); -+ const expandedRow = page.locator("[data-game-expanded-row='lantern-reef']"); -+ await expect(expandedRow).toHaveCount(1); -+ await expect(expandedRow.locator("[data-game-child-table]")).toHaveCount(3); ++ const sourceIdeaTable = expandedRows.nth(0).locator("[data-game-child-table='source-idea']"); + await expect(sourceIdeaTable.locator("caption")).toHaveText("Source Idea"); + await expect(sourceIdeaTable.locator("tbody tr")).toHaveText([ + "IdeaLantern Reef", +@@ -446,7 +453,7 @@ test("Game Hub validates Open Games parent and child tables", async ({ page }) = + await expect(sourceIdeaTable.locator("button, input, textarea, select, [contenteditable='true'], [role='button']")).toHaveCount(0); + await expect(sourceIdeaTable).not.toContainText(/Edit|Delete|Current Focus|Recommended Next Tool/); + +- const readinessOutputTable = expandedRow.locator("[data-game-child-table='readiness-output']"); ++ const readinessOutputTable = expandedRows.nth(1).locator("[data-game-child-table='readiness-output']"); + await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output"); + await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]); + await expect(readinessOutputTable).not.toContainText(/Guide reef keepers|Keep traversal gentle|Use warm lantern art/); +@@ -681,46 +688,43 @@ test("Game Hub displays and edits game purpose and member role", async ({ page } + } + }); + +-test("Game Hub progress panels update from mock game state", async ({ page }) => { ++test("Game Hub readiness child rows update from mock game state", async ({ page }) => { + const failures = await openRepoPage(page, "/toolbox/game-hub/index.html", { session: creatorSession() }); + + try { +- await expect(page.locator("[data-game-status]")).toHaveText("Under Construction"); +- await expect(page.locator("[data-game-progress]")).toHaveText("Demo Game identity ready"); +- await expect(page.locator("[data-publishing-progress]")).toHaveText("Publish blocked until configuration and required assets are ready"); +- await expect(page.locator("[data-current-focus]")).toHaveText("Complete Game Configuration"); + await expect(page.locator("[data-recommended-next-tool]").first()).toHaveText("Game Configuration"); +- await expect(page.locator("[data-game-progress-checklist]")).toContainText("Game identity: Complete"); +- await expect(page.locator("[data-game-output-panels] summary")).toHaveText([ +- "Readiness Output" ++ await expect(page.locator("[data-source-idea-section]")).toHaveCount(0); ++ await expect(page.locator("[data-game-output-panels]")).toHaveCount(0); ++ await expect(page.locator("[data-game-hub-foundation]")).toHaveCount(0); + -+ const sourceIdeaTable = expandedRow.locator("[data-game-child-table='source-idea']"); -+ await expect(sourceIdeaTable.locator("caption")).toHaveText("Source Idea"); -+ await expect(sourceIdeaTable.locator("tbody tr")).toHaveText([ ++ const demoGameRow = page.locator("[data-game-row='demo-game']"); ++ await demoGameRow.locator("[data-game-toggle='demo-game']").click(); ++ let readinessOutputTable = page.locator("[data-game-expanded-row='demo-game'][data-game-child-row='readiness-output'] [data-game-child-table='readiness-output']"); ++ await expect(readinessOutputTable.locator("tbody tr")).toHaveText([ ++ "Game StatusUnder Construction", ++ "Game ProgressDemo Game identity ready", ++ "Launch ProgressPublish blocked until configuration and required assets are ready", ++ "Current FocusComplete Game Configuration", ++ "Recommended Next ToolGame Configuration", ++ "Game identityComplete", ++ "Game configurationUnder Construction", ++ "Playable buildPlanned", ++ "Publishing reviewPlanned", + ]); +- await expect(page.locator("aside.tool-column").last().getByText("Readiness Output")).toHaveCount(0); +- const panelOrderIsCorrect = await page.locator(".tool-center-panel").evaluate((panel) => { +- const projectInformation = panel.querySelector("[data-game-project-information]"); +- const sourceIdea = panel.querySelector("[data-source-idea-section]"); +- const staticOverlay = panel.querySelector("[data-game-hub-foundation]"); +- const outputPanels = panel.querySelector("[data-game-output-panels]"); +- return Boolean( +- projectInformation && +- sourceIdea && +- staticOverlay && +- outputPanels && +- (projectInformation.compareDocumentPosition(sourceIdea) & Node.DOCUMENT_POSITION_FOLLOWING) && +- (sourceIdea.compareDocumentPosition(staticOverlay) & Node.DOCUMENT_POSITION_FOLLOWING) && +- (staticOverlay.compareDocumentPosition(outputPanels) & Node.DOCUMENT_POSITION_FOLLOWING) +- ); +- }); +- expect(panelOrderIsCorrect).toBe(true); + + await page.getByLabel("Game Name").fill("Progress Review Game"); + await page.getByRole("button", { name: "Create Game" }).click(); +- await expect(page.locator("[data-game-status]")).toHaveText("Under Construction"); +- await expect(page.locator("[data-game-progress]")).toHaveText("Progress Review Game identity ready"); + await expect(page.locator("[data-game-project-information]")).toContainText("Progress Review Game"); ++ const progressReviewRow = page.locator("[data-game-row='progress-review-game-1']"); ++ await progressReviewRow.locator("[data-game-toggle='progress-review-game-1']").click(); ++ readinessOutputTable = page.locator("[data-game-expanded-row='progress-review-game-1'][data-game-child-row='readiness-output'] [data-game-child-table='readiness-output']"); ++ await expect(readinessOutputTable).toContainText("Progress Review Game identity ready"); + + await page.getByRole("button", { name: "Delete Open Game" }).click(); + await expect(page.locator("[data-active-game-name]")).toHaveText("Demo Game"); +- await expect(page.locator("[data-game-progress]")).toHaveText("Demo Game identity ready"); ++ await demoGameRow.locator("[data-game-toggle='demo-game']").click(); ++ readinessOutputTable = page.locator("[data-game-expanded-row='demo-game'][data-game-child-row='readiness-output'] [data-game-child-table='readiness-output']"); ++ await expect(readinessOutputTable).toContainText("Demo Game identity ready"); + + await expectNoPageFailures(failures); + } finally { +diff --git a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs +index f861f1f44..2c7b70b5d 100644 +--- a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs ++++ b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs +@@ -387,15 +387,18 @@ test("Idea Board uses accordion table ideas and notes", async ({ page }) => { + await expect(page.getByRole("heading", { level: 1, name: "Game Hub" })).toBeVisible(); + await expect(page.locator("[data-active-game-name]")).toHaveText("Lantern Reef"); + await expect(page.locator("[data-game-list]")).toContainText("Lantern Reef"); +- await expect(page.locator("[data-source-idea-display]")).toHaveText("Lantern Reef"); +- await expect(page.locator("[data-source-idea-pitch]")).toHaveText("Guide light through a reef that rearranges at dusk."); +- await expect(page.locator("[data-source-idea-notes]")).toContainText("Use dusk tide changes as the first Game Hub planning note."); +- await expect(page.locator("[data-source-idea-section] :is(input, textarea, select, button)")).toHaveCount(0); ++ await expect(page.locator("[data-source-idea-section]")).toHaveCount(0); ++ await expect(page.locator("[data-game-output-panels]")).toHaveCount(0); ++ await expect(page.locator("[data-game-hub-foundation]")).toHaveCount(0); + await expect(page.getByRole("button", { name: "Delete Open Game" })).toHaveCount(0); + const activeGameRow = page.locator("[data-game-row][data-game-active='true']"); + await expect(activeGameRow).toContainText("Lantern Reef"); + await activeGameRow.locator("[data-game-toggle]").click(); +- const sourceIdeaChildTable = page.locator("[data-game-expanded-row] [data-game-child-table='source-idea']"); ++ let expandedRows = page.locator("[data-game-expanded-row]"); ++ await expect(expandedRows).toHaveCount(2); ++ await expect(expandedRows.nth(0)).toHaveAttribute("data-game-child-row", "source-idea"); ++ await expect(expandedRows.nth(1)).toHaveAttribute("data-game-child-row", "readiness-output"); ++ let sourceIdeaChildTable = expandedRows.nth(0).locator("[data-game-child-table='source-idea']"); + await expect(sourceIdeaChildTable.locator("caption")).toHaveText("Source Idea"); + await expect(sourceIdeaChildTable.locator("thead th")).toHaveText(["Context", "Details"]); + await expect(sourceIdeaChildTable.locator("tbody tr")).toHaveText([ +@@ -404,13 +407,22 @@ test("Idea Board uses accordion table ideas and notes", async ({ page }) => { + "Note 1Use dusk tide changes as the first Game Hub planning note.", + ]); + await expect(sourceIdeaChildTable.locator(":is(input, textarea, select, button)")).toHaveCount(0); ++ await expect(expandedRows.nth(1).locator("[data-game-child-table='readiness-output'] caption")).toHaveText("Readiness Output"); + await page.reload({ waitUntil: "networkidle" }); + await expect(page.locator("[data-active-game-name]")).toHaveText("Lantern Reef"); + await expect(page.locator("[data-game-list]")).toContainText("Lantern Reef"); +- await expect(page.locator("[data-source-idea-display]")).toHaveText("Lantern Reef"); +- await expect(page.locator("[data-source-idea-pitch]")).toHaveText("Guide light through a reef that rearranges at dusk."); +- await expect(page.locator("[data-source-idea-notes]")).toContainText("Use dusk tide changes as the first Game Hub planning note."); +- await expect(page.locator("[data-source-idea-section] :is(input, textarea, select, button)")).toHaveCount(0); ++ await expect(page.locator("[data-source-idea-section]")).toHaveCount(0); ++ await expect(page.locator("[data-game-output-panels]")).toHaveCount(0); ++ await expect(page.locator("[data-game-hub-foundation]")).toHaveCount(0); ++ await activeGameRow.locator("[data-game-toggle]").click(); ++ expandedRows = page.locator("[data-game-expanded-row]"); ++ await expect(expandedRows).toHaveCount(2); ++ sourceIdeaChildTable = expandedRows.nth(0).locator("[data-game-child-table='source-idea']"); ++ await expect(sourceIdeaChildTable.locator("tbody tr")).toHaveText([ + "IdeaLantern Reef", -+ "PitchGuide reef keepers through dusk storms.", -+ "Note 1Keep traversal gentle.", -+ "Note 2Use warm lantern art.", ++ "PitchGuide light through a reef that rearranges at dusk.", ++ "Note 1Use dusk tide changes as the first Game Hub planning note.", + ]); -+ await expect(sourceIdeaTable.locator("button, input, textarea, select, [contenteditable='true'], [role='button']")).toHaveCount(0); -+ await expect(sourceIdeaTable).not.toContainText(/Edit|Delete|Current Focus|Recommended Next Tool/); -+ -+ const readinessOutputTable = expandedRow.locator("[data-game-child-table='readiness-output']"); -+ await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output"); -+ await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]); -+ await expect(readinessOutputTable).not.toContainText(/Guide reef keepers|Keep traversal gentle|Use warm lantern art/); -+ await expect(readinessOutputTable.locator("[data-readiness-checklist-row] th")).toHaveText(journeyBuckets); -+ -+ await expect(page.locator("[data-game-list] [data-game-list-status='empty']")).toHaveCount(0); -+ await expect(page.locator("[data-game-list] [data-game-list-status='unavailable']")).toHaveCount(0); -+ await expectNoPageFailures(failures); -+ } finally { -+ await failures.server.close(); -+ } -+}); -+ - test("Game Hub preserves guest browsing and blocks guest saves", async ({ page }) => { - const failures = await openRepoPage(page, "/toolbox/game-hub/index.html"); + await expect(page.getByRole("button", { name: "Delete Open Game" })).toHaveCount(0); + await expect(page.locator("main")).not.toContainText(/\bproject records\b|\bAPI\b|\bDB\b|\bmock\b|\bseed\b|\bdebug\b|\binternal\b/i); + await page.getByRole("link", { name: "Open Game Journey" }).click(); +diff --git a/toolbox/game-hub/game-hub.js b/toolbox/game-hub/game-hub.js +index 4b21814d7..029936527 100644 +--- a/toolbox/game-hub/game-hub.js ++++ b/toolbox/game-hub/game-hub.js +@@ -27,10 +27,6 @@ const elements = { + projectRecordStatus: document.querySelector("[data-project-record-status]"), + projectRecordsTable: document.querySelector("[data-project-records-table]"), + purposeInput: document.querySelector("[data-game-purpose-input]"), +- sourceIdeaDisplay: document.querySelector("[data-source-idea-display]"), +- sourceIdeaName: document.querySelector("[data-source-idea-name]"), +- sourceIdeaNotes: document.querySelector("[data-source-idea-notes]"), +- sourceIdeaPitch: document.querySelector("[data-source-idea-pitch]"), + gameStatus: document.querySelector("[data-game-status]"), + gameStatusInput: document.querySelector("[data-game-status-input]"), + publishingProgress: document.querySelector("[data-publishing-progress]"), +@@ -241,36 +237,11 @@ function createGameToggleButton(game, expanded) { + button.type = "button"; + button.dataset.gameToggle = game.id; + button.setAttribute("aria-expanded", String(expanded)); +- button.setAttribute("aria-controls", `game-child-${game.id}`); ++ button.setAttribute("aria-controls", `game-child-source-idea-${game.id} game-child-readiness-output-${game.id}`); + button.textContent = game.name; + return button; + } + +-function renderGameSummaryChildTable(parent, game) { +- const wrapper = document.createElement("div"); +- wrapper.className = "table-wrapper"; +- const table = document.createElement("table"); +- table.className = "data-table data-table--fixed"; +- table.dataset.gameChildTable = "summary"; +- table.setAttribute("aria-label", `${game.name} game summary`); +- table.innerHTML = "
Review the open project and its source idea.
++Review the open project, then expand its game row to see Source Idea and Readiness Output.
+| Idea | No source idea yet |
|---|---|
| Pitch | Create a project from Idea Board to see source details. |
| Notes |
|
Under Construction
Demo Game identity ready
Publish blocked until configuration and required assets are ready
Complete Game Configuration
Game Configuration
| Path | Status | Next Tool |
|---|---|---|
| Plan | Under Construction | Game Configuration |
| Configure | Planned | Build Game |
| Release | Planned | Publish |
Review the open project and its source idea.
+Review the open project, then expand its game row to see Source Idea and Readiness Output.
| Idea | No source idea yet |
|---|---|
| Pitch | Create a project from Idea Board to see source details. |
| Notes |
|
Under Construction
Demo Game identity ready
Publish blocked until configuration and required assets are ready
Complete Game Configuration
Game Configuration
| Path | Status | Next Tool |
|---|---|---|
| Plan | Under Construction | Game Configuration |
| Configure | Planned | Build Game |
| Release | Planned | Publish |