From 958b521625a19e6a0fc2a0cdf3fb315e1e4ed5fa Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 23 Jun 2026 20:36:36 -0400 Subject: [PATCH] PR_26175_BRAVO_001: add PR 003 Messages code review --- ...3-messages-emotion-profiles-code-review.md | 200 + .../dev/reports/codex_changed_files.txt | 307 +- docs_build/dev/reports/codex_review.diff | 37151 +--------------- 3 files changed, 663 insertions(+), 36995 deletions(-) create mode 100644 docs_build/dev/reports/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review.md diff --git a/docs_build/dev/reports/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review.md b/docs_build/dev/reports/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review.md new file mode 100644 index 000000000..861b62cce --- /dev/null +++ b/docs_build/dev/reports/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review.md @@ -0,0 +1,200 @@ +# PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review + +## Scope + +OWNER override approved: review GitHub PR #3 only. + +This report reviews GitHub PR #3, `Pr/PR 26171 006 message emotion profile management`, for the Messages / Emotion Profiles area. It is report-only. No runtime code was changed, no PR was merged or closed, and no branch was deleted. + +## Source Evidence + +- GitHub PR: https://github.com/ToolboxAid/HTML-JavaScript-Gaming/pull/3 +- PR state: open, non-draft, not merged +- GitHub mergeable state: false +- Base branch: `main` +- Head branch: `pr/PR_26171_006-message-emotion-profile-management` +- Base SHA: `64231546e8f6a20810d23c590a9787eed40f63a1` +- Head SHA: `6b83cedd5196c8117ec6deb69f73e91f04080573` +- Review date: 2026-06-24 +- Current `main` at review start: `5415f6675d7a0f10931b83368948a83df98d8021` + +## Changed Code Files Only + +| File | Status | Additions | Deletions | Review Summary | +| --- | --- | ---: | ---: | --- | +| `src/dev-runtime/messages/messages-sqlite-service.mjs` | modified | 54 | 4 | Adds emotion-profile usage queries, reference payloads, aggregate usage counts, and a referenced-profile deactivation guard. | +| `tests/playwright/tools/MessagesTool.spec.mjs` | modified | 37 | 0 | Adds targeted coverage for usage counts and referenced-profile deactivation rejection. | +| `toolbox/messages/index.html` | modified | 5 | 4 | Adds a Usage column to the Emotion Profiles table and changes numeric input step precision. | +| `toolbox/messages/messages.js` | modified | 7 | 1 | Renders usage count and reloads emotion profiles after segment reloads. | + +## Full Changed File List + +| File | Status | Additions | Deletions | +| --- | --- | ---: | ---: | +| `docs_build/dev/reports/PR_26171_006-message-emotion-profile-management-manual-validation.md` | added | 11 | 0 | +| `docs_build/dev/reports/PR_26171_006-message-emotion-profile-management-validation.txt` | added | 30 | 0 | +| `docs_build/dev/reports/PR_26171_006-message-emotion-profile-management.md` | added | 53 | 0 | +| `docs_build/dev/reports/codex_changed_files.txt` | modified | 8 | 9 | +| `docs_build/dev/reports/codex_review.diff` | modified | 352 | 305 | +| `docs_build/pr/BUILD_PR_26171_006-message-emotion-profile-management.md` | added | 83 | 0 | +| `docs_build/pr/BUILD_PR_26171_008-message-tts-profile-foundation.md` | added | 98 | 0 | +| `docs_build/pr/BUILD_PR_26171_010-message-speech-preview.md` | added | 84 | 0 | +| `docs_build/pr/BUILD_PR_26171_012-message-voice-provider-adapters.md` | added | 86 | 0 | +| `docs_build/pr/BUILD_PR_26171_014-runtime-message-playback-foundation.md` | added | 84 | 0 | +| `docs_build/pr/BUILD_PR_26171_016-midi-studio-roadmap-foundation.md` | added | 74 | 0 | +| `src/dev-runtime/messages/messages-sqlite-service.mjs` | modified | 54 | 4 | +| `tests/playwright/tools/MessagesTool.spec.mjs` | modified | 37 | 0 | +| `toolbox/messages/index.html` | modified | 5 | 4 | +| `toolbox/messages/messages.js` | modified | 7 | 1 | + +## Findings + +### P1 - PR #3 is stale against current Messages persistence + +The primary service change targets `src/dev-runtime/messages/messages-sqlite-service.mjs`, but current `main` no longer has that file. Current `main` has `src/dev-runtime/messages/messages-postgres-service.mjs` and routes Messages through the PostgreSQL service in `src/dev-runtime/server/local-api-router.mjs`. + +Current Project Instructions also state that SQLite is deprecated, new persistence work targets PostgreSQL, and PRs introducing SQLite should be rejected. Merging PR #3 as-is would either fail to apply cleanly or reintroduce a deprecated service path. This is a blocking merge risk. + +### P1 - The PR mixes the PR_006 implementation with future workstream BUILD specs + +PR #3 adds PR_008, PR_010, PR_012, PR_014, and PR_016 BUILD files alongside the PR_006 implementation. GitHub already has a P1 review comment asking to split these future specs out of this PR. This violates the one-purpose PR rule and can pollute later review lanes with unmerged planning changes. + +### P2 - API payload expands reference details and should be reviewed against the current contract + +The patch adds `references`, `messageUsageCount`, `segmentUsageCount`, and `usageCount` to emotion profile payloads. That is a reasonable product direction, but it changes the API shape and exposes message and segment labels/previews in the profile response. In current main, similar behavior exists in the PostgreSQL service, so PR #3 should not be the vehicle for this API contract. + +### P2 - Targeted validation passed, but the required workspace lane failed + +The PR report says targeted syntax, direct service probing, and a Messages Playwright path passed. It also says `npm run test:workspace-v2` failed. Even if those failures were outside the Messages scope at the time, the current merge decision should not ignore that validation gap. + +## Runtime Impact + +Runtime impact is high if merged as-is. The PR changes the Messages service layer, API payloads surfaced through the Messages tool, and UI behavior. It also touches a SQLite service file that is absent on current `main`, while the current app uses PostgreSQL-backed Messages code. + +## DB/API Impact + +- Adds two usage queries per emotion profile in the SQLite service. +- Adds aggregate usage fields to emotion profile API payloads. +- Adds `references` details containing message and segment reference information. +- Blocks active-to-inactive changes when a profile has references. +- Does not add a delete endpoint. +- Conflicts with the current PostgreSQL-only persistence direction. + +## UI Impact + +- Adds a Usage column to the Emotion Profiles table. +- Updates the loading row colspan from 3 to 4. +- Renders usage count in `toolbox/messages/messages.js`. +- Refreshes emotion profile state and select options after segment reload. +- Changes volume, pitch, and rate input step values from `0.05` to `0.01`. + +UI risk is moderate. The usage column is useful, but table layout and edit/select state should be retested against the current Theme V2 Messages UI. + +## Theme V2 Compliance + +PASS with review note. The code patch does not add inline styles, inline scripts, page-local CSS, or tool-local CSS. The UI changes are table markup and existing DOM rendering changes. A visual check is still recommended because the added Usage column changes table density. + +## Auth/Session Impact + +No direct auth or session files are touched. The PR uses existing Local API paths. Auth/session risk is low, but any future rework should validate the Messages tool under the current session model because current workspace validation previously reported session-related failures outside the Messages scope. + +## Browser-Owned Data Risk + +Low direct risk. The patch does not add `localStorage`, browser-owned persistence, or persisted browser-only message state. Data remains server-owned through the Local API/service boundary. + +## Deletion/In-Use Guard Risk + +The active-to-inactive guard is directionally correct: referenced Emotion Profiles should not be deactivated while messages or segments still reference them. Remaining risks: + +- The guard lives in the stale SQLite service in PR #3. +- There is no database-level constraint shown in the PR. +- The PR does not address delete behavior beyond saying no delete endpoint was added. +- The current PostgreSQL service already contains a comparable in-use guard, so the PR is superseded for this behavior. + +## Test Coverage + +Covered in PR #3: + +- Usage counts for one message reference and one segment reference. +- API rejection when deactivating a referenced `Urgent` profile. +- UI diagnostic when attempting to deactivate that referenced profile. +- Active status remains after rejected deactivation. + +Not sufficiently covered for merge: + +- Current PostgreSQL service path on `main`. +- Empty and multi-profile usage aggregation. +- Unreferenced profile deactivation remains allowed. +- Table layout or visual regression after adding the Usage column. +- Full workspace lane is not green in the PR evidence. + +## Playwright Recommendation + +Do not rely on the old PR #3 Playwright result for a current merge. If the behavior is still desired, retest on a fresh PostgreSQL-based branch from current `main` with: + +- Targeted Messages tool Playwright coverage for usage count display. +- API/service test coverage for referenced and unreferenced Emotion Profile active-state changes. +- A visual/table-density check for the Emotion Profiles table. +- The current workspace validation lane required by Project Instructions. + +## Merge/Hold/Close Recommendation + +Recommendation: close PR #3 as superseded after OWNER approval. + +Reason: + +- It is not mergeable on GitHub. +- It targets a removed SQLite service path. +- Current governance says PostgreSQL is the active persistence direction. +- Current `main` already has Messages PostgreSQL usage-count and in-use guard behavior. +- It mixes future BUILD specs into a PR_006 implementation branch. + +Do not merge PR #3. If owner wants any missing behavior from PR #3 retained, create a fresh Bravo branch from current `main` and implement only the missing PostgreSQL-backed Messages/Emotion Profiles behavior. + +## Requirement Checklist + +| Requirement | Result | Notes | +| --- | --- | --- | +| Start from main | PASS | Started from clean `main` at `5415f6675d7a0f10931b83368948a83df98d8021`. | +| Hard stop if not main, dirty, or unsynced | PASS | Gate passed: branch `main`, clean worktree, local/origin `0 0`. | +| Read all Project Instructions | PASS | Read `docs_build/dev/ProjectInstructions/` before report generation. | +| Review GitHub PR #3 only | PASS | Report covers only PR #3. | +| Pull PR #3 diff | PASS | Used GitHub PR metadata and file diff data for PR #3. | +| Include changed code files only | PASS | Included dedicated changed-code-files table. | +| Include full changed file list | PASS | Included all 15 changed files with status and line counts. | +| Include runtime impact | PASS | Included. | +| Include DB/API impact | PASS | Included. | +| Include UI impact | PASS | Included. | +| Include Theme V2 compliance | PASS | Included. | +| Include auth/session impact | PASS | Included. | +| Include browser-owned data risk | PASS | Included. | +| Include deletion/in-use guard risk | PASS | Included. | +| Include test coverage | PASS | Included. | +| Include Playwright recommendation | PASS | Included. | +| Include merge/hold/close recommendation | PASS | Included: close as superseded after OWNER approval. | +| Do not merge PR #3 | PASS | No merge performed. | +| Do not close PR #3 | PASS | No close performed. | +| Do not delete branches | PASS | No branch deletion performed. | +| Do not modify runtime code | PASS | Only report artifacts changed. | +| Create repo-structured ZIP under `tmp/` | PASS | Required path: `tmp/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review_delta.zip`. | + +## Validation Lane Report + +| Validation | Result | Evidence | +| --- | --- | --- | +| Branch gate | PASS | Branch created after clean/synced `main` gate passed. | +| GitHub PR metadata | PASS | Fetched PR #3 metadata from GitHub. | +| GitHub PR file list | PASS | Fetched all changed files for PR #3 from GitHub. | +| Current-main persistence comparison | PASS | Confirmed current `main` has `messages-postgres-service.mjs` and does not have `messages-sqlite-service.mjs`. | +| Report-only scope check | PASS | Only required report files changed. | +| Runtime tests | NOT RUN | Report-only PR; no runtime code changed. | +| Playwright tests | NOT RUN | Report-only PR; no UI/runtime code changed. | +| Samples | NOT RUN | Report-only PR; no samples changed. | + +## Manual Validation Notes + +- Confirmed PR #3 remains open and non-draft. +- Confirmed PR #3 is not mergeable on GitHub. +- Confirmed PR #3 has an existing P1 review note for mixed future BUILD specs. +- Confirmed current Project Instructions prohibit new SQLite persistence work. +- Confirmed current main already contains PostgreSQL-backed Messages usage count and referenced-profile guard behavior. diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index 6eccfd73d..983c838e7 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,304 +1,3 @@ -# git status --short -M .env.example -M .gitignore -M account/user-controls-page.js -M admin/infrastructure.html -M admin/system-health.html -R toolbox/assets/assets-api-client.js -> assets/js/shared/assets-api-client.js -R toolbox/controls/controls-api-client.js -> assets/js/shared/controls-api-client.js -R toolbox/game-journey/game-journey-api-client.js -> assets/js/shared/game-journey-api-client.js -A assets/js/shared/status.js -R toolbox/text-to-speech/tts-profile-store.js -> assets/js/shared/tts-profile-store.js -M assets/theme-v2/css/status.css -M assets/theme-v2/css/tables.css -M assets/theme-v2/js/admin-infrastructure.js -M assets/theme-v2/js/admin-invitations.js -M assets/theme-v2/js/admin-operations.js -M assets/theme-v2/js/admin-setup-actions.js -M assets/theme-v2/js/admin-system-health.js -M assets/theme-v2/js/gamefoundry-partials.js -A assets/theme-v2/js/toolbox-status-bar.js -R toolbox/assets/assets-upload-worker.js -> assets/toolbox/assets/js/assets-upload-worker.js -R toolbox/assets/assets.js -> assets/toolbox/assets/js/index.js -R toolbox/colors/colors.js -> assets/toolbox/colors/js/index.js -R toolbox/controls/controls.js -> assets/toolbox/controls/js/index.js -R toolbox/events/events.js -> assets/toolbox/events/js/index.js -R toolbox/game-configuration/game-configuration.js -> assets/toolbox/game-configuration/js/index.js -R toolbox/game-design/game-design.js -> assets/toolbox/game-design/js/index.js -R toolbox/game-journey/game-journey.js -> assets/toolbox/game-journey/js/index.js -R toolbox/idea-board/index.js -> assets/toolbox/idea-board/js/index.js -R toolbox/objects/objects.js -> assets/toolbox/objects/js/index.js -R toolbox/tags/tags.js -> assets/toolbox/tags/js/index.js -R toolbox/text-to-speech/text2speech.js -> assets/toolbox/text-to-speech/js/index.js -M docs_build/dev/BUILD_PR.md -M docs_build/dev/ProjectInstructions/PROJECT_INSTRUCTIONS.md -M docs_build/dev/ProjectInstructions/TEAM_START_COMMANDS.md -M docs_build/dev/ProjectInstructions/addendums/multi_team.md -M docs_build/dev/ProjectInstructions/addendums/table_first_ui.md -M docs_build/dev/ProjectInstructions/addendums/team_start_and_release.md -M docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md -M docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md -M docs_build/dev/ProjectInstructions/team_assignments/team_ownership.md -A docs_build/dev/reports/PR_26172_CHARLIE_001-repository-compliance-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_002-test-results-artifact-cleanup.md -A docs_build/dev/reports/PR_26172_CHARLIE_003-src-dev-runtime-test-migration-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_004-src-dev-runtime-low-risk-test-move.md -A docs_build/dev/reports/PR_26172_CHARLIE_005-scattered-tool-js-css-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_006-low-risk-tool-asset-migration.md -A docs_build/dev/reports/PR_26172_CHARLIE_006A-game-journey-validation-failure-investigation.md -A docs_build/dev/reports/PR_26172_CHARLIE_007-shared-js-candidate-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_008-canonical-structure-guardrail-script.md -A docs_build/dev/reports/PR_26172_CHARLIE_009-guardrail-preflight-wireup.md -A docs_build/dev/reports/PR_26172_CHARLIE_010-shared-js-low-risk-consolidation.md -A docs_build/dev/reports/PR_26172_CHARLIE_011-test-location-remediation.md -A docs_build/dev/reports/PR_26172_CHARLIE_012-canonical-structure-enforcement-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_013-tool-js-css-canonical-migration-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_014-low-risk-tool-migration-1.md -A docs_build/dev/reports/PR_26172_CHARLIE_015-low-risk-tool-migration-2.md -A docs_build/dev/reports/PR_26172_CHARLIE_016-repository-compliance-reaudit.md -A docs_build/dev/reports/PR_26172_CHARLIE_017-tool-js-css-canonical-migration-audit-2.md -A docs_build/dev/reports/PR_26172_CHARLIE_018-low-risk-tool-migration-3.md -A docs_build/dev/reports/PR_26172_CHARLIE_019-low-risk-tool-migration-4.md -A docs_build/dev/reports/PR_26172_CHARLIE_020-low-risk-tool-migration-5.md -A docs_build/dev/reports/PR_26172_CHARLIE_021-repository-compliance-reaudit-2.md -A docs_build/dev/reports/PR_26172_CHARLIE_022-target-tool-migration-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_023-target-tool-migration-batch-1.md -A docs_build/dev/reports/PR_26172_CHARLIE_024-target-tool-migration-batch-2.md -A docs_build/dev/reports/PR_26172_CHARLIE_025-target-tool-migration-batch-3.md -A docs_build/dev/reports/PR_26172_CHARLIE_026-target-tool-migration-batch-4.md -A docs_build/dev/reports/PR_26172_CHARLIE_027-target-tool-compliance-reaudit.md -A docs_build/dev/reports/PR_26172_CHARLIE_028-remaining-target-tool-migration-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_029-controls-canonical-js-migration.md -A docs_build/dev/reports/PR_26172_CHARLIE_030-assets-tool-canonical-js-migration-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_031-assets-tool-safe-entrypoint-migration.md -A docs_build/dev/reports/PR_26172_CHARLIE_032-assets-worker-api-client-migration-or-exception.md -A docs_build/dev/reports/PR_26172_CHARLIE_033-game-journey-canonical-js-migration-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_034-game-journey-safe-entrypoint-migration.md -A docs_build/dev/reports/PR_26172_CHARLIE_035-game-journey-api-client-migration-or-exception.md -A docs_build/dev/reports/PR_26172_CHARLIE_036-final-target-tool-compliance-reaudit.md -A docs_build/dev/reports/PR_26172_CHARLIE_037-retained-exceptions-audit.md -A docs_build/dev/reports/PR_26172_CHARLIE_038-assets-upload-http-500-investigation.md -A docs_build/dev/reports/PR_26172_CHARLIE_039-assets-api-worker-exception-resolution.md -A docs_build/dev/reports/PR_26172_CHARLIE_040-controls-game-journey-api-client-resolution.md -A docs_build/dev/reports/PR_26172_CHARLIE_041-final-retained-exceptions-reaudit.md -A docs_build/dev/reports/PR_26172_CHARLIE_042-canonical-tool-worker-placement.md -A docs_build/dev/reports/PR_26172_CHARLIE_043-final-charlie-compliance-reaudit.md -A docs_build/dev/reports/PR_26172_OWNER_040-four-team-backlog-alignment.md -A docs_build/dev/reports/PR_26172_OWNER_041-four-team-cleanup.md -A docs_build/dev/reports/PR_26174_ALFA_000-projectinstructions-archive-ignore-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_000-projectinstructions-archive-ignore-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_000-projectinstructions-archive-ignore-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_000-projectinstructions-archive-ignore-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_000-projectinstructions-archive-ignore.md -A docs_build/dev/reports/PR_26174_ALFA_001-idea-board-create-project-api-contract-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_001-idea-board-create-project-api-contract-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_001-idea-board-create-project-api-contract-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_001-idea-board-create-project-api-contract-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_001-idea-board-create-project-api-contract.md -A docs_build/dev/reports/PR_26174_ALFA_002-game-hub-project-intake-display-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_002-game-hub-project-intake-display-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_002-game-hub-project-intake-display-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_002-game-hub-project-intake-display-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_002-game-hub-project-intake-display.md -A docs_build/dev/reports/PR_26174_ALFA_003-game-hub-journey-bootstrap-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_003-game-hub-journey-bootstrap-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_003-game-hub-journey-bootstrap-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_003-game-hub-journey-bootstrap-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_003-game-hub-journey-bootstrap.md -A docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model.md -A docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish.md -A docs_build/dev/reports/PR_26174_ALFA_006-game-hub-empty-and-error-states-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_006-game-hub-empty-and-error-states-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_006-game-hub-empty-and-error-states-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_006-game-hub-empty-and-error-states-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_006-game-hub-empty-and-error-states.md -A docs_build/dev/reports/PR_26174_ALFA_007-game-journey-count-ui-polish-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_007-game-journey-count-ui-polish-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_007-game-journey-count-ui-polish-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_007-game-journey-count-ui-polish-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_007-game-journey-count-ui-polish.md -A docs_build/dev/reports/PR_26174_ALFA_008-alpha-stack-final-validation-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_008-alpha-stack-final-validation-final-stack-report.md -A docs_build/dev/reports/PR_26174_ALFA_008-alpha-stack-final-validation-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_008-alpha-stack-final-validation-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_008-alpha-stack-final-validation-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_008-alpha-stack-final-validation.md -A docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout.md -A docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish.md -A docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table.md -A docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation.md -A docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_013-game-hub-game-row-child-rows.md -A docs_build/dev/reports/PR_26174_ALFA_014-game-hub-parent-columns-center-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_014-game-hub-parent-columns-center-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_014-game-hub-parent-columns-center-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_014-game-hub-parent-columns-center-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_014-game-hub-parent-columns-center.md -A docs_build/dev/reports/PR_26174_ALFA_015-game-hub-actions-and-setup-cleanup-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_015-game-hub-actions-and-setup-cleanup-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_015-game-hub-actions-and-setup-cleanup-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_015-game-hub-actions-and-setup-cleanup-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_015-game-hub-actions-and-setup-cleanup.md -A docs_build/dev/reports/PR_26174_ALFA_016-game-hub-row-edit-add-selected-state-branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_016-game-hub-row-edit-add-selected-state-manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_016-game-hub-row-edit-add-selected-state-requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_016-game-hub-row-edit-add-selected-state-validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_016-game-hub-row-edit-add-selected-state.md -A docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup.md -A docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup_branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup_manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup_requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup_validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state.md -A docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label.md -A docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label_branch-validation.txt -A docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label_manual-validation-notes.txt -A docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label_requirement-checklist.txt -A docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label_validation-lane.txt -A docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_branch-validation.md -A docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_manual-validation-notes.md -A docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_report.md -A docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_requirements-checklist.md -A docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_validation-lane.md -A docs_build/dev/reports/PR_26174_ALFA_021-idea-board-status-filter-table-polish_branch-validation.md -A docs_build/dev/reports/PR_26174_ALFA_021-idea-board-status-filter-table-polish_manual-validation-notes.md -A docs_build/dev/reports/PR_26174_ALFA_021-idea-board-status-filter-table-polish_report.md -A docs_build/dev/reports/PR_26174_ALFA_021-idea-board-status-filter-table-polish_requirements-checklist.md -A docs_build/dev/reports/PR_26174_ALFA_021-idea-board-status-filter-table-polish_validation-lane.md -A docs_build/dev/reports/PR_26174_ALFA_022-idea-board-status-dropdown-fix_branch-validation.md -A docs_build/dev/reports/PR_26174_ALFA_022-idea-board-status-dropdown-fix_manual-validation-notes.md -A docs_build/dev/reports/PR_26174_ALFA_022-idea-board-status-dropdown-fix_report.md -A docs_build/dev/reports/PR_26174_ALFA_022-idea-board-status-dropdown-fix_requirements-checklist.md -A docs_build/dev/reports/PR_26174_ALFA_022-idea-board-status-dropdown-fix_validation-lane.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-final-closeout_branch-validation.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-final-closeout_manual-validation-notes.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-final-closeout_report.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-final-closeout_requirements-checklist.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-final-closeout_validation-lane.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-workstream-closeout_branch-validation.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-workstream-closeout_manual-validation-notes.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-workstream-closeout_report.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-workstream-closeout_requirements-checklist.md -A docs_build/dev/reports/PR_26174_ALFA_EOD-workstream-closeout_validation-lane.md -A docs_build/dev/reports/PR_26174_ALFA_MERGE_PUSH_CLOSEOUT.md -A docs_build/dev/reports/PR_26175_ALFA_001-toolbox-selected-game-status-bar_report.md -A docs_build/dev/reports/PR_26175_ALFA_001-toolbox-selected-game-status-bar_requirements-checklist.md -A docs_build/dev/reports/PR_26175_ALFA_001-toolbox-selected-game-status-bar_validation-lane.md -A docs_build/dev/reports/PR_26175_ALFA_002-toolbox-status-bar-context-polish_report.md -A docs_build/dev/reports/PR_26175_ALFA_002-toolbox-status-bar-context-polish_requirements-checklist.md -A docs_build/dev/reports/PR_26175_ALFA_002-toolbox-status-bar-context-polish_validation-lane.md -A docs_build/dev/reports/PR_26175_ALFA_004-game-hub-completion-status-audit_report.md -A docs_build/dev/reports/PR_26175_ALFA_004-game-hub-completion-status-audit_requirements-checklist.md -A docs_build/dev/reports/PR_26175_ALFA_004-game-hub-completion-status-audit_validation-lane.md -A docs_build/dev/reports/PR_26175_ALFA_005-game-hub-audit-findings-cleanup_report.md -A docs_build/dev/reports/PR_26175_ALFA_005-game-hub-audit-findings-cleanup_requirements-checklist.md -A docs_build/dev/reports/PR_26175_ALFA_005-game-hub-audit-findings-cleanup_validation-lane.md -A docs_build/dev/reports/PR_26175_ALFA_006-game-hub-create-project-validation_report.md -A docs_build/dev/reports/PR_26175_ALFA_006-game-hub-create-project-validation_requirements-checklist.md -A docs_build/dev/reports/PR_26175_ALFA_006-game-hub-create-project-validation_validation-lane.md -A docs_build/dev/reports/PR_26175_ALFA_008-game-hub-feature-matrix_report.md -A docs_build/dev/reports/PR_26175_ALFA_008-game-hub-feature-matrix_requirements-checklist.md -A docs_build/dev/reports/PR_26175_ALFA_008-game-hub-feature-matrix_validation-lane.md -A docs_build/dev/reports/PR_26175_CHARLIE_002-system-health-dashboard-instruction-compliance-checklist.md -A docs_build/dev/reports/PR_26175_CHARLIE_002-system-health-dashboard-manual-validation-notes.md -A docs_build/dev/reports/PR_26175_CHARLIE_002-system-health-dashboard.md -A docs_build/dev/reports/PR_26175_CHARLIE_002-system-health-dashboard_PLAN.md -A docs_build/dev/reports/PR_26175_CHARLIE_003-r2-storage-standardization-instruction-compliance-checklist.md -A docs_build/dev/reports/PR_26175_CHARLIE_003-r2-storage-standardization-manual-validation-notes.md -A docs_build/dev/reports/PR_26175_CHARLIE_003-r2-storage-standardization.md -A docs_build/dev/reports/PR_26175_CHARLIE_003-r2-storage-standardization_PLAN.md -A docs_build/dev/reports/PR_26175_CHARLIE_EOD-branch-validation.md -A docs_build/dev/reports/PR_26175_CHARLIE_EOD-closeout.md -A docs_build/dev/reports/PR_26175_CHARLIE_EOD-merge-summary.md -M docs_build/dev/reports/codex_changed_files.txt -M docs_build/dev/reports/codex_review.diff -M package.json -M scripts/run-targeted-test-lanes.mjs -M scripts/validate-browser-env-agnostic.mjs -A scripts/validate-canonical-repository-structure.mjs -M scripts/validate-storage-config.mjs -M src/dev-runtime/persistence/mock-db-store.js -M src/dev-runtime/persistence/tool-repositories/game-journey-mock-repository.js -M src/dev-runtime/server/local-api-router.mjs -M src/dev-runtime/storage/storage-config.mjs -M src/shared/toolbox/tool-metadata-inventory.js -M tests/dev-runtime/AdminHealthOperations.test.mjs -M tests/dev-runtime/ProductDataProviderContractHardening.test.mjs -A tests/dev-runtime/StorageConfig.test.mjs -R tests/core/FixedTicker.test.mjs -> tests/engine/core/FixedTicker.test.mjs -R tests/core/FrameClock.test.mjs -> tests/engine/core/FrameClock.test.mjs -R tests/core/RuntimeMetrics.test.mjs -> tests/engine/core/RuntimeMetrics.test.mjs -M tests/playwright/tools/AdminHealthOperationsPage.spec.mjs -M tests/playwright/tools/AdminPlatformToolsWireframes.spec.mjs -M tests/playwright/tools/AssetToolMockRepository.spec.mjs -M tests/playwright/tools/BuildPathProgressSimplification.spec.mjs -M tests/playwright/tools/GameConfigurationMockRepository.spec.mjs -M tests/playwright/tools/GameDesignMockRepository.spec.mjs -M tests/playwright/tools/GameHubMockRepository.spec.mjs -M tests/playwright/tools/GameJourneyTool.spec.mjs -M tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -M tests/playwright/tools/InputMappingV2Tool.spec.mjs -M tests/playwright/tools/ObjectsTool.spec.mjs -M tests/playwright/tools/ToolboxAdminMetadataSsot.spec.mjs -M tests/playwright/tools/ToolboxRoutePages.spec.mjs -A tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs -A tests/regression/CanonicalRepositoryStructureGuardrail.test.mjs -M tests/run-tests.mjs -M tests/tools/MessagesPlaybackSource.test.mjs -M tests/tools/Text2SpeechShell.test.mjs -M toolbox/assets/index.html -M toolbox/colors/index.html -D toolbox/colors/palette-api-client.js -M toolbox/controls/index.html -M toolbox/events/index.html -D toolbox/game-configuration/game-configuration-api-client.js -M toolbox/game-configuration/index.html -D toolbox/game-design/game-design-api-client.js -M toolbox/game-design/index.html -M toolbox/game-hub/game-hub.js -M toolbox/game-hub/index.html -M toolbox/game-journey/index.html -M toolbox/idea-board/index.html -M toolbox/messages/messages.js -M toolbox/objects/index.html -D toolbox/objects/objects-api-client.js -M toolbox/tags/index.html -D toolbox/tags/tags-api-client.js -M toolbox/text-to-speech/index.html - -# git ls-files --others --exclude-standard -(no output) - -# git diff --stat -(no output) \ No newline at end of file +docs_build/dev/reports/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review.md +docs_build/dev/reports/codex_changed_files.txt +docs_build/dev/reports/codex_review.diff diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 04dbf2e15..b2f8e8257 100644 --- a/docs_build/dev/reports/codex_review.diff +++ b/docs_build/dev/reports/codex_review.diff @@ -1,36750 +1,519 @@ -diff --git a/.env.example b/.env.example -index f293b6f83..9d5e4301b 100644 ---- a/.env.example -+++ b/.env.example -@@ -50,6 +50,11 @@ GAMEFOUNDRY_DB_BACKUP_DIR= - - # Server-only project asset storage configuration. - # Browser uploads must go through the server API and must not receive these secrets. -+# Approved GAMEFOUNDRY_STORAGE_PROJECTS_PREFIX values: -+# DEV /dev/projects/ -+# IST /ist/projects/ -+# UAT /uat/projects/ -+# PRD /prod/projects/ - GAMEFOUNDRY_STORAGE_ENDPOINT= - GAMEFOUNDRY_STORAGE_ACCESS_KEY_ID= - -diff --git a/.gitignore b/.gitignore -index ec5f65503..8f4247376 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -13,10 +13,12 @@ - # Ignore the NEXT_COMMAND.txt file - NEXT_COMMAND.txt - --# Ignore the tests results and temporary files -+# Ignore generated dependency, test result, and temporary files - node_modules/ - tests/results/ -+tests/results/** - tmp/test-results/ -+tmp/test-results/** - tmp/ - scripts/untracked/ - projects/ -diff --git a/account/user-controls-page.js b/account/user-controls-page.js -index 02fb2c117..d42be3309 100644 ---- a/account/user-controls-page.js -+++ b/account/user-controls-page.js -@@ -1,4 +1,4 @@ --import { createControlsToolApiRepository } from "../toolbox/controls/controls-api-client.js"; -+import { createControlsToolApiRepository } from "../assets/js/shared/controls-api-client.js"; - import { getSessionCurrent } from "../src/api/session-api-client.js"; - import InputService from "../src/engine/input/InputService.js"; - import InputCaptureService from "../src/engine/input/InputCaptureService.js"; -diff --git a/admin/infrastructure.html b/admin/infrastructure.html -index 0c9e90b3e..89bf99e0d 100644 ---- a/admin/infrastructure.html -+++ b/admin/infrastructure.html -@@ -41,7 +41,7 @@ -

/dev/projects/

-

/ist/projects/

-

/uat/projects/

--

/prd/projects/

-+

/prod/projects/

- - - -@@ -81,7 +81,7 @@ - DEV/dev/projects/Loading - IST/ist/projects/Loading - UAT/uat/projects/Loading -- PRD/prd/projects/Loading -+ PRD/prod/projects/Loading - - - -diff --git a/admin/system-health.html b/admin/system-health.html -index f5330bcdd..65ff3c12e 100644 ---- a/admin/system-health.html -+++ b/admin/system-health.html -@@ -39,6 +39,7 @@ - Health Sections -
-

Environment Summary

-+

Local API Startup

-

Database Health

-

Storage Health

-

Runtime Environment

-@@ -74,6 +75,21 @@ - - -
-+
-+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+
Local API Startup Diagnostics
FieldSafe ValueStatus
Startup diagnosticsWaiting for safe API statusPENDING
-+
-
- - -diff --git a/toolbox/assets/assets-api-client.js b/assets/js/shared/assets-api-client.js -similarity index 95% -rename from toolbox/assets/assets-api-client.js -rename to assets/js/shared/assets-api-client.js -index 9e504899c..1232aac7c 100644 ---- a/toolbox/assets/assets-api-client.js -+++ b/assets/js/shared/assets-api-client.js -@@ -3,7 +3,7 @@ import { - createServerRepositoryClient, - readServerToolConstants, - requireServerConstant, --} from "../../src/api/server-api-client.js"; -+} from "../../../src/api/server-api-client.js"; - - const constants = readServerToolConstants("assets"); - -diff --git a/toolbox/controls/controls-api-client.js b/assets/js/shared/controls-api-client.js -similarity index 96% -rename from toolbox/controls/controls-api-client.js -rename to assets/js/shared/controls-api-client.js -index 89eb688be..3b382022b 100644 ---- a/toolbox/controls/controls-api-client.js -+++ b/assets/js/shared/controls-api-client.js -@@ -2,7 +2,7 @@ import { - createServerRepositoryClient, - readServerToolConstants, - requireServerConstant, --} from "../../src/api/server-api-client.js"; -+} from "../../../src/api/server-api-client.js"; - - const constants = readServerToolConstants("controls"); - -diff --git a/toolbox/game-journey/game-journey-api-client.js b/assets/js/shared/game-journey-api-client.js -similarity index 91% -rename from toolbox/game-journey/game-journey-api-client.js -rename to assets/js/shared/game-journey-api-client.js -index 4d9647bb2..fba67b725 100644 ---- a/toolbox/game-journey/game-journey-api-client.js -+++ b/assets/js/shared/game-journey-api-client.js -@@ -2,11 +2,11 @@ import { - createServerRepositoryClient, - readServerToolConstants, - requireServerConstant, --} from "../../src/api/server-api-client.js"; -+} from "../../../src/api/server-api-client.js"; - export { - readGameJourneyCompletionMetrics, - updateGameJourneyCompletionMetric, --} from "../../src/api/game-journey-completion-api-client.js"; -+} from "../../../src/api/game-journey-completion-api-client.js"; - - const constants = readServerToolConstants("game-journey"); - -diff --git a/assets/js/shared/status.js b/assets/js/shared/status.js +diff --git a/docs_build/dev/reports/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review.md b/docs_build/dev/reports/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review.md new file mode 100644 -index 000000000..26bb6926f +index 000000000..861b62cce --- /dev/null -+++ b/assets/js/shared/status.js -@@ -0,0 +1,42 @@ -+export const STATUS_VALUES = Object.freeze(["PASS", "WARN", "FAIL", "PENDING", "INFO", "SKIP"]); -+ -+export function statusText(value, fallback = "not available") { -+ const text = String(value ?? "").trim(); -+ return text || fallback; -+} -+ -+export function normalizeStatusValue(value, fallback = "PENDING") { -+ const normalized = String(value || "").trim().toUpperCase(); -+ return STATUS_VALUES.includes(normalized) ? normalized : fallback; -+} -+ -+export function formatStatusMessage(status, message, options = {}) { -+ const normalized = normalizeStatusValue(status, options.fallbackStatus || "PENDING"); -+ const resolvedMessage = Object.hasOwn(options, "fallbackMessage") -+ ? statusText(message, options.fallbackMessage) -+ : String(message); -+ return `${normalized}: ${resolvedMessage}`; -+} -+ -+export function formatStatusReason(status, reason, options = {}) { -+ const normalized = normalizeStatusValue(status, options.fallbackStatus || "PENDING"); -+ return `${normalized}: ${statusText(reason, options.fallbackReason || "Safe server diagnostics did not provide a reason.")}`; -+} -+ -+export function applyStatusNode(node, status, options = {}) { -+ if (!node) { -+ return ""; -+ } -+ const normalized = normalizeStatusValue(status, options.fallbackStatus || "PENDING"); -+ node.textContent = normalized; -+ node.dataset.healthStatus = normalized; -+ if (normalized === "PASS" && !options.reason) { -+ node.removeAttribute("title"); -+ node.removeAttribute("aria-label"); -+ return normalized; -+ } -+ const reason = statusText(options.reason, options.titleFallback || "Safe server diagnostics returned this non-PASS status."); -+ node.setAttribute("title", `${options.titlePrefix || "Reason: "}${reason}`); -+ node.setAttribute("aria-label", formatStatusReason(normalized, options.reason, options)); -+ return normalized; -+} -diff --git a/toolbox/text-to-speech/tts-profile-store.js b/assets/js/shared/tts-profile-store.js -similarity index 100% -rename from toolbox/text-to-speech/tts-profile-store.js -rename to assets/js/shared/tts-profile-store.js -diff --git a/assets/theme-v2/css/status.css b/assets/theme-v2/css/status.css -index 5c521c45d..88e0d5e64 100644 ---- a/assets/theme-v2/css/status.css -+++ b/assets/theme-v2/css/status.css -@@ -35,6 +35,119 @@ - line-height: var(--line-height-copy) - } - -+.toolbox-status-bar { -+ width: 100%; -+ border-block: var(--border-standard); -+ background: var(--panel-overlay-strong); -+ color: var(--text) -+} -+ -+.toolbox-status-bar__inner { -+ width: var(--container-width); -+ margin: var(--space-0) auto; -+ padding: var(--space-10) var(--space-0); -+ display: grid; -+ grid-template-columns: minmax(var(--space-0), 1fr) minmax(var(--space-0), 2fr); -+ gap: var(--space-16); -+ align-items: center -+} -+ -+.toolbox-status-bar__game { -+ min-width: var(--space-0); -+ display: flex; -+ align-items: center; -+ flex-wrap: wrap; -+ gap: var(--space-14); -+ text-align: left -+} -+ -+.toolbox-status-bar__field { -+ min-width: var(--space-0); -+ display: grid; -+ gap: var(--space-3) -+} -+ -+.toolbox-status-bar__label { -+ color: var(--muted); -+ font-size: var(--font-size-xs); -+ font-weight: var(--font-weight-heavy); -+ letter-spacing: var(--letter-spacing-kicker); -+ text-transform: uppercase -+} -+ -+.toolbox-status-bar__game-name { -+ color: var(--text); -+ font-size: var(--font-size-base); -+ overflow-wrap: anywhere -+} -+ -+.toolbox-status-bar__purpose { -+ color: var(--muted); -+ font-size: var(--font-size-sm); -+ overflow-wrap: anywhere -+} -+ -+.toolbox-status-bar__center { -+ min-width: var(--space-0); -+ display: flex; -+ align-items: center; -+ justify-content: center; -+ flex-wrap: wrap; -+ gap: var(--space-10); -+ text-align: center -+} -+ -+.toolbox-status-bar__context-type { -+ flex: 0 0 auto -+} -+ -+.toolbox-status-bar__message { -+ margin: var(--space-0); -+ max-width: var(--measure-lg); -+ overflow-wrap: anywhere -+} -+ -+.toolbox-status-bar__action { -+ flex: 0 0 auto -+} -+ -+.toolbox-status-bar[data-selected-game-state="active"] { -+ border-color: color-mix(in srgb, var(--green) 52%, var(--line)) -+} -+ -+.toolbox-status-bar[data-selected-game-state="missing"] { -+ border-color: var(--gold-border-muted) -+} -+ -+.toolbox-status-bar[data-selected-game-state="error"] { -+ border-color: color-mix(in srgb, var(--red) 52%, var(--line)) -+} -+ -+.toolbox-status-bar[data-toolbox-status-context-kind="error"] .toolbox-status-bar__context-type { -+ border-color: color-mix(in srgb, var(--red) 62%, var(--line)); -+ color: var(--red) -+} -+ -+.toolbox-status-bar[data-toolbox-status-context-kind="warning"] .toolbox-status-bar__context-type, -+.toolbox-status-bar[data-toolbox-status-context-kind="validation"] .toolbox-status-bar__context-type { -+ border-color: var(--gold-border-muted); -+ color: var(--gold) -+} -+ -+.toolbox-status-bar[data-toolbox-status-context-kind="save"] .toolbox-status-bar__context-type { -+ border-color: color-mix(in srgb, var(--green) 62%, var(--line)); -+ color: var(--green) -+} -+ -+body.tool-focus-mode .toolbox-status-bar { -+ position: fixed; -+ inset-block-end: var(--space-0); -+ inset-inline: var(--space-0); -+ z-index: var(--z-index-lg); -+ border-block-end: var(--space-0); -+ box-shadow: var(--shadow-md) -+} -+ - .platform-banner { - width: 100%; - border-bottom: var(--border-standard); -@@ -144,3 +257,16 @@ - color: var(--bg); - text-shadow: none - } -+ -+@media (max-width: 720px) { -+ .toolbox-status-bar__inner { -+ width: var(--container-width); -+ grid-template-columns: 1fr; -+ text-align: center -+ } -+ -+ .toolbox-status-bar__game { -+ justify-content: center; -+ text-align: center -+ } -+} -diff --git a/assets/theme-v2/css/tables.css b/assets/theme-v2/css/tables.css -index c39ef19f0..5575ff684 100644 ---- a/assets/theme-v2/css/tables.css -+++ b/assets/theme-v2/css/tables.css -@@ -116,26 +116,26 @@ td { - } - - .idea-board-idea-label { -- display: inline-flex; -- align-items: center; -- gap: .35em; -+ display: inline; - color: inherit; - font: inherit; - line-height: var(--line-height-single); - vertical-align: baseline; -- white-space: nowrap -+ white-space: normal - } - - .idea-board-idea-label__text { -- line-height: var(--line-height-single) -+ overflow-wrap: anywhere; -+ line-height: inherit - } - - .idea-board-idea-chevron { - display: inline-block; - width: 1em; - height: 1em; -- flex: 0 0 1em; -+ margin-right: .35em; - background: currentColor; -+ vertical-align: -0.125em; - -webkit-mask-position: center; - mask-position: center; - -webkit-mask-repeat: no-repeat; -diff --git a/assets/theme-v2/js/admin-infrastructure.js b/assets/theme-v2/js/admin-infrastructure.js -index 0251c32d6..b470c84df 100644 ---- a/assets/theme-v2/js/admin-infrastructure.js -+++ b/assets/theme-v2/js/admin-infrastructure.js -@@ -6,7 +6,7 @@ const STORAGE_PATH_LANES = Object.freeze([ - Object.freeze({ lane: "DEV", path: "/dev/projects/" }), - Object.freeze({ lane: "IST", path: "/ist/projects/" }), - Object.freeze({ lane: "UAT", path: "/uat/projects/" }), -- Object.freeze({ lane: "PRD", path: "/prd/projects/" }), -+ Object.freeze({ lane: "PRD", path: "/prod/projects/" }), - ]); - - class AdminInfrastructureStoragePathStatus { -diff --git a/assets/theme-v2/js/admin-invitations.js b/assets/theme-v2/js/admin-invitations.js -index 5299093f3..3e3c9e829 100644 ---- a/assets/theme-v2/js/admin-invitations.js -+++ b/assets/theme-v2/js/admin-invitations.js -@@ -3,6 +3,7 @@ import { - readAdminInvites, - revokeAdminBetaInvite - } from "../../../src/api/admin-invitations-api-client.js"; -+import { formatStatusMessage } from "../../js/shared/status.js"; - - function text(value) { - if (value === undefined || value === null || value === "") { -@@ -53,7 +54,7 @@ class AdminInvitesController { - } - - setStatus(status, message) { -- this.status.textContent = `${status}: ${message}`; -+ this.status.textContent = formatStatusMessage(status, message); - } - - setSummary(payload = {}) { -diff --git a/assets/theme-v2/js/admin-operations.js b/assets/theme-v2/js/admin-operations.js -index 306faca9b..fd669562f 100644 ---- a/assets/theme-v2/js/admin-operations.js -+++ b/assets/theme-v2/js/admin-operations.js -@@ -2,6 +2,7 @@ import { - readAdminOperationsStatus, - runAdminOperationAction - } from "../../../src/api/admin-operations-api-client.js"; -+import { formatStatusMessage } from "../../js/shared/status.js"; - - class AdminOperationsController { - constructor(root) { -@@ -25,7 +26,7 @@ class AdminOperationsController { - } - - setStatus(status, message) { -- this.status.textContent = `${status}: ${message}`; -+ this.status.textContent = formatStatusMessage(status, message); - } - - createLabeledControl(labelText, control) { -diff --git a/assets/theme-v2/js/admin-setup-actions.js b/assets/theme-v2/js/admin-setup-actions.js -index 27ddbd1aa..84f39fcd9 100644 ---- a/assets/theme-v2/js/admin-setup-actions.js -+++ b/assets/theme-v2/js/admin-setup-actions.js -@@ -1,11 +1,12 @@ - import { readAdminSetupStatus } from "../../../src/api/admin-setup-api-client.js"; -+import { formatStatusMessage } from "../../js/shared/status.js"; - - const refreshButtons = Array.from(document.querySelectorAll("[data-admin-setup-refresh]")); - const statusFields = Array.from(document.querySelectorAll("[data-admin-setup-status]")); - const statusRows = Array.from(document.querySelectorAll("[data-admin-setup-status-rows]")); - - function setStatus(message, status = "PASS") { -- const text = `${status}: ${message}`; -+ const text = formatStatusMessage(status, message); - statusFields.forEach((field) => { - field.textContent = text; - }); -diff --git a/assets/theme-v2/js/admin-system-health.js b/assets/theme-v2/js/admin-system-health.js -index dfe67df79..72ddd7ea8 100644 ---- a/assets/theme-v2/js/admin-system-health.js -+++ b/assets/theme-v2/js/admin-system-health.js -@@ -2,8 +2,12 @@ import { - readAdminSystemHealthStatus, - runAdminSystemHealthStorageConnectivityAction, - } from "../../../src/api/admin-system-health-api-client.js"; -+import { -+ applyStatusNode, -+ normalizeStatusValue, -+ statusText, -+} from "../../js/shared/status.js"; - --const STATUS_VALUES = Object.freeze(["PASS", "WARN", "FAIL", "PENDING", "INFO", "SKIP"]); - const STORAGE_DIAGNOSTIC_ACTIONS = Object.freeze([ - Object.freeze({ actionId: "storage-list", key: "list" }), - Object.freeze({ actionId: "storage-write-test-object", key: "write" }), -@@ -12,18 +16,7 @@ const STORAGE_DIAGNOSTIC_ACTIONS = Object.freeze([ - ]); - - function asText(value, fallback = "not available") { -- const text = String(value ?? "").trim(); -- return text || fallback; --} -- --function statusValue(value, fallback = "PENDING") { -- const normalized = String(value || "").trim().toUpperCase(); -- return STATUS_VALUES.includes(normalized) ? normalized : fallback; --} -- --function reasonText(status, reason) { -- const message = asText(reason, "Safe server diagnostics did not provide a reason."); -- return `${status}: ${message}`; -+ return statusText(value, fallback); - } - - class AdminSystemHealthController { -@@ -45,6 +38,7 @@ class AdminSystemHealthController { - node.dataset.adminSystemHealthStorageStatus, - node, - ])); -+ this.startupRows = root.querySelector("[data-admin-system-health-startup-rows]"); - this.runtimeRows = root.querySelector("[data-admin-system-health-runtime-rows]"); - } - -@@ -80,26 +74,14 @@ class AdminSystemHealthController { - } - - setStatusNode(node, status, reason = "") { -- if (!node) { -- return; -- } -- const normalized = statusValue(status); -- node.textContent = normalized; -- node.dataset.healthStatus = normalized; -- if (normalized === "PASS" && !reason) { -- node.removeAttribute("title"); -- node.removeAttribute("aria-label"); -- return; -- } -- const resolvedReason = reasonText(normalized, reason); -- node.setAttribute("title", `Reason: ${asText(reason, "Safe server diagnostics returned this non-PASS status.")}`); -- node.setAttribute("aria-label", resolvedReason); -+ applyStatusNode(node, status, { reason }); - } - - renderPending(reason) { - ["host", "database", "migration", "connection"].forEach((key) => { - this.setStatus(key, "PENDING", reason); - }); -+ this.renderStartupPending(reason); - this.renderStoragePending(reason); - } - -@@ -138,6 +120,45 @@ class AdminSystemHealthController { - this.setStorageStatus("bucket", storageStatus.bucketStatus || storageStatus.status, reason); - } - -+ renderStartupPending(reason) { -+ if (!this.startupRows) { -+ return; -+ } -+ const row = document.createElement("tr"); -+ row.append( -+ this.createCell("Local API startup diagnostics"), -+ this.createCell("not available"), -+ this.createStatusCell("PENDING", reason), -+ ); -+ this.startupRows.replaceChildren(row); -+ } -+ -+ renderStartupDiagnostics(localApiStartup = {}) { -+ if (!this.startupRows) { -+ return; -+ } -+ if (localApiStartup?.secretsExposed === true || localApiStartup?.secretEditingAllowed === true) { -+ this.renderStartupPending("Safe Local API startup diagnostics were blocked because the response exposed secret controls."); -+ return; -+ } -+ const rows = Array.isArray(localApiStartup.rows) ? localApiStartup.rows : []; -+ if (!rows.length) { -+ this.renderStartupPending("Safe Local API startup diagnostics returned no rows."); -+ return; -+ } -+ const fragment = document.createDocumentFragment(); -+ rows.forEach((startupRow) => { -+ const row = document.createElement("tr"); -+ row.append( -+ this.createCell(startupRow.field), -+ this.createCell(startupRow.value), -+ this.createStatusCell(startupRow.status, startupRow.reason || localApiStartup.message), -+ ); -+ fragment.append(row); -+ }); -+ this.startupRows.replaceChildren(fragment); -+ } -+ - storageResultTarget(result = {}) { - if (typeof result.keysListed === "number" && result.actionId === "storage-list") { - return `${result.keysListed} object(s) under ${asText(result.projectsPrefix, "configured prefix")}`; -@@ -176,7 +197,7 @@ class AdminSystemHealthController { - - createStatusCell(status, reason) { - const cell = document.createElement("td"); -- cell.dataset.healthStatus = statusValue(status); -+ cell.dataset.healthStatus = normalizeStatusValue(status); - this.setStatusNode(cell, status, reason); - return cell; - } -@@ -230,6 +251,7 @@ class AdminSystemHealthController { - return; - } - this.renderPostgresStatus(data?.databaseStatus || {}); -+ this.renderStartupDiagnostics(data?.localApiStartup || {}); - this.renderStorageStatus(data?.storageStatus || {}); - this.runStorageDiagnostics(); - this.renderRuntimeEnvironment(data?.runtimeEnvironment || {}); -diff --git a/assets/theme-v2/js/gamefoundry-partials.js b/assets/theme-v2/js/gamefoundry-partials.js -index 44b301916..f7f220880 100644 ---- a/assets/theme-v2/js/gamefoundry-partials.js -+++ b/assets/theme-v2/js/gamefoundry-partials.js -@@ -1048,6 +1048,32 @@ - } - } - -+ async function renderToolboxStatusBar() { -+ const pagePath = currentPagePath() || ""; -+ if (pagePath.indexOf("toolbox/") !== 0) { -+ return; -+ } -+ try { -+ const module = await import(assetUrl("js/toolbox-status-bar.js")); -+ if (typeof module.mountToolboxStatusBar === "function") { -+ module.mountToolboxStatusBar({ -+ gameHubHref: routeHref("game-hub"), -+ pagePath -+ }); -+ } -+ } catch (error) { -+ console.warn("[toolbox/status] Shared status bar could not be loaded.", error); -+ } -+ } -+ -+ function refreshSharedSurfaces() { -+ renderPlatformBanner() -+ .then(renderToolboxStatusBar) -+ .catch(function (error) { -+ console.error(error); -+ }); -+ } -+ - enforcePageProtection(); - document.addEventListener("DOMContentLoaded", function () { - enforcePageProtection(); -@@ -1056,11 +1082,11 @@ - replaceExisting("header-nav", "header.site-header"), - replaceExisting("footer", "footer.footer") - ]; -- Promise.all(tasks).then(renderPlatformBanner).catch(function (error) { -+ Promise.all(tasks).then(refreshSharedSurfaces).catch(function (error) { - console.error(error); - }); - }); - window.addEventListener("gamefoundry:session-user-changed", refreshHeaderLoginState); - window.addEventListener("gamefoundry:data-changed", refreshHeaderOnly); -- window.addEventListener("gamefoundry:platform-settings-changed", renderPlatformBanner); -+ window.addEventListener("gamefoundry:platform-settings-changed", refreshSharedSurfaces); - }()); -diff --git a/assets/theme-v2/js/toolbox-status-bar.js b/assets/theme-v2/js/toolbox-status-bar.js -new file mode 100644 -index 000000000..fdb367328 ---- /dev/null -+++ b/assets/theme-v2/js/toolbox-status-bar.js -@@ -0,0 +1,348 @@ -+import { createServerRepositoryClient } from "/src/api/server-api-client.js"; -+ -+const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]); -+const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]"; -+ -+let repository = null; -+let messageObserver = null; -+let listenersInstalled = false; -+let latestToolMessage = ""; -+let pendingToolMessageRefresh = 0; -+let mountOptions = { -+ gameHubHref: "toolbox/game-hub/index.html", -+ pagePath: "", -+}; -+ -+function getRepository() { -+ if (!repository) { -+ repository = createServerRepositoryClient("game-hub"); -+ } -+ return repository; -+} -+ -+function toolSlugFromPath(pagePath) { -+ const parts = String(pagePath || window.location.pathname || "") -+ .replace(/^\/+/, "") -+ .split("/") -+ .filter(Boolean); -+ if (parts[0] !== "toolbox") { -+ return ""; -+ } -+ const slug = (parts[1] || "index").replace(/\.html$/i, ""); -+ return slug || "index"; -+} -+ -+function pageRequiresSelectedGame() { -+ const slug = toolSlugFromPath(mountOptions.pagePath); -+ return !EXCLUDED_SELECTED_GAME_TOOLS.has(slug); -+} -+ -+function isRepositoryError(value) { -+ return Boolean(value && typeof value === "object" && value.error === true); -+} -+ -+function normalizeSelectedGame(value) { -+ if (!value) { -+ return null; -+ } -+ if (isRepositoryError(value)) { -+ throw new Error(value.message || "Game Hub selected game is unavailable."); -+ } -+ const id = String(value.id || "").trim(); -+ const name = String(value.name || "").trim(); -+ if (!id || !name || !Array.isArray(value.members)) { -+ throw new Error("Game Hub selected game payload is malformed."); -+ } -+ return Object.freeze({ -+ id, -+ name, -+ ownerKey: String(value.ownerKey || "").trim(), -+ purpose: String(value.purpose || "Game").trim() || "Game", -+ status: String(value.status || "").trim(), -+ }); -+} -+ -+function readSelectedGame() { -+ return normalizeSelectedGame(getRepository().getActiveGame()); -+} -+ -+function createText(tagName, className, datasetName) { -+ const element = document.createElement(tagName); -+ if (className) { -+ element.className = className; -+ } -+ if (datasetName) { -+ element.dataset[datasetName] = ""; -+ } -+ return element; -+} -+ -+function createStatusBar() { -+ const bar = document.createElement("section"); -+ bar.className = "toolbox-status-bar"; -+ bar.dataset.toolboxStatusBar = ""; -+ bar.setAttribute("aria-label", "Toolbox selected game status"); -+ -+ const inner = document.createElement("div"); -+ inner.className = "toolbox-status-bar__inner"; -+ -+ const game = document.createElement("div"); -+ game.className = "toolbox-status-bar__game"; -+ game.dataset.toolboxSelectedGame = ""; -+ -+ const nameField = document.createElement("div"); -+ nameField.className = "toolbox-status-bar__field"; -+ nameField.dataset.toolboxSelectedGameNameField = ""; -+ const nameLabel = createText("span", "toolbox-status-bar__label", "toolboxSelectedGameNameLabel"); -+ nameLabel.textContent = "Selected Game Name"; -+ const name = createText("strong", "toolbox-status-bar__game-name", "toolboxSelectedGameName"); -+ nameField.append(nameLabel, name); -+ -+ const purposeField = document.createElement("div"); -+ purposeField.className = "toolbox-status-bar__field"; -+ purposeField.dataset.toolboxSelectedGamePurposeField = ""; -+ const purposeLabel = createText("span", "toolbox-status-bar__label", "toolboxSelectedGamePurposeLabel"); -+ purposeLabel.textContent = "Selected Game Purpose"; -+ const purpose = createText("span", "toolbox-status-bar__purpose", "toolboxSelectedGamePurpose"); -+ purpose.dataset.toolboxSelectedGameMeta = ""; -+ purposeField.append(purposeLabel, purpose); -+ game.append(nameField, purposeField); -+ -+ const center = document.createElement("div"); -+ center.className = "toolbox-status-bar__center"; -+ center.dataset.toolboxStatusCenter = ""; -+ -+ const contextType = createText("span", "pill toolbox-status-bar__context-type", "toolboxStatusContextType"); -+ const message = createText("p", "toolbox-status-bar__message status", "toolboxStatusMessage"); -+ message.setAttribute("role", "status"); -+ const action = document.createElement("a"); -+ action.className = "btn btn--compact toolbox-status-bar__action"; -+ action.dataset.toolboxStatusAction = ""; -+ action.href = mountOptions.gameHubHref; -+ action.textContent = "Open Game Hub"; -+ center.append(contextType, message, action); -+ -+ inner.append(game, center); -+ bar.append(inner); -+ return bar; -+} -+ -+function ensureStatusBar() { -+ let bar = document.querySelector(STATUS_BAR_SELECTOR); -+ if (!bar) { -+ bar = createStatusBar(); -+ } -+ placeStatusBar(bar); -+ return bar; -+} -+ -+function placeStatusBar(bar) { -+ const footer = document.querySelector("footer.footer"); -+ if (footer?.parentNode) { -+ if (bar.nextElementSibling !== footer) { -+ footer.before(bar); -+ } -+ return; -+ } -+ -+ const main = document.querySelector("main"); -+ if (main?.parentNode) { -+ main.after(bar); -+ return; -+ } -+ -+ document.body.append(bar); -+} -+ -+function visibleStatusText(element) { -+ if (!element || element.closest(STATUS_BAR_SELECTOR) || element.hidden) { -+ return ""; -+ } -+ if (element.closest("[hidden]")) { -+ return ""; -+ } -+ return String(element.textContent || "").replace(/\s+/g, " ").trim(); -+} -+ -+function readToolMessage() { -+ const messages = Array.from(document.querySelectorAll("main [role='status'], main .status")) -+ .map((element, index) => ({ -+ index, -+ priority: Object.keys(element.dataset || {}).length > 0 ? 1 : 0, -+ text: visibleStatusText(element), -+ })) -+ .filter((entry) => entry.text); -+ const prioritized = messages -+ .filter((entry) => entry.priority > 0) -+ .pop(); -+ return prioritized?.text || messages[messages.length - 1]?.text || ""; -+} -+ -+function updateLatestToolMessage() { -+ const nextMessage = readToolMessage(); -+ if (nextMessage && nextMessage !== latestToolMessage) { -+ latestToolMessage = nextMessage; -+ refreshToolboxStatusBar(); -+ } -+} -+ -+function scheduleToolMessageRefresh() { -+ window.clearTimeout(pendingToolMessageRefresh); -+ pendingToolMessageRefresh = window.setTimeout(updateLatestToolMessage, 0); -+ window.setTimeout(updateLatestToolMessage, 120); -+} -+ -+function observeToolMessages() { -+ messageObserver?.disconnect(); -+ const main = document.querySelector("main"); -+ if (!main) { -+ return; -+ } -+ latestToolMessage = readToolMessage(); -+ messageObserver = new MutationObserver(updateLatestToolMessage); -+ messageObserver.observe(main, { -+ characterData: true, -+ childList: true, -+ subtree: true, -+ }); -+} -+ -+function publishSelectedGameContext(selectedGame, state) { -+ const required = pageRequiresSelectedGame(); -+ const context = Object.freeze({ -+ required, -+ selectedGame, -+ source: "game-hub", -+ state, -+ }); -+ window.GameFoundryToolboxSelectedGame = context; -+ document.body.dataset.toolboxSelectedGameFilter = required ? state : "optional"; -+ document.body.dataset.toolboxSelectedGameSource = "game-hub"; -+ if (selectedGame) { -+ document.body.dataset.toolboxSelectedGameId = selectedGame.id; -+ } else { -+ delete document.body.dataset.toolboxSelectedGameId; -+ } -+ document.body.classList.toggle("toolbox-selected-game-missing", required && state === "missing"); -+ document.body.classList.toggle("toolbox-selected-game-unavailable", required && state === "error"); -+ document.body.classList.toggle("toolbox-selected-game-optional", !required); -+ window.dispatchEvent(new CustomEvent("gamefoundry:toolbox-selected-game-context", { -+ detail: context, -+ })); -+} -+ -+function classifyToolContext(messageText, state, required) { -+ const text = String(messageText || "").trim(); -+ if (state === "error") { -+ return { kind: "error", label: "Error" }; -+ } -+ if (required && state === "missing") { -+ return { kind: "action", label: "Tool Action" }; -+ } -+ if (/\b(error|failed|malformed|unavailable|could not)\b/i.test(text)) { -+ return { kind: "error", label: "Error" }; -+ } -+ if (/\b(sign in|refresh|try again|temporarily|blocked)\b/i.test(text)) { -+ return { kind: "warning", label: "Warning" }; -+ } -+ if (/\b(validation|requirement|requirements|missing|required|open or seed)\b/i.test(text)) { -+ return { kind: "validation", label: "Validation" }; -+ } -+ if (/\b(saved|created|deleted|updated|loaded|save changes)\b/i.test(text)) { -+ return { kind: "save", label: "Save State" }; -+ } -+ return { kind: "action", label: "Tool Action" }; -+} -+ -+function renderSelectedGame(bar, selectedGame, state, messageText) { -+ const required = pageRequiresSelectedGame(); -+ const name = bar.querySelector("[data-toolbox-selected-game-name]"); -+ const purpose = bar.querySelector("[data-toolbox-selected-game-purpose]"); -+ const contextType = bar.querySelector("[data-toolbox-status-context-type]"); -+ const message = bar.querySelector("[data-toolbox-status-message]"); -+ const action = bar.querySelector("[data-toolbox-status-action]"); -+ const nextMessage = messageText || latestToolMessage || (selectedGame -+ ? `Tool context is filtered to ${selectedGame.name}.` -+ : required -+ ? "Select or create a game in Game Hub before using this toolbox page." -+ : "Idea Board can capture ideas before a Game Hub game exists."); -+ const context = classifyToolContext(nextMessage, state, required); -+ -+ bar.dataset.selectedGameState = state; -+ bar.dataset.selectedGameRequired = String(required); -+ bar.dataset.toolboxStatusContextKind = context.kind; -+ contextType.textContent = context.label; -+ action.hidden = false; -+ action.href = mountOptions.gameHubHref; -+ -+ if (selectedGame) { -+ name.textContent = selectedGame.name; -+ purpose.textContent = selectedGame.purpose || "Game"; -+ message.textContent = nextMessage; -+ action.textContent = "Open Game Hub"; -+ return; -+ } -+ -+ if (!required) { -+ name.textContent = "No game selected"; -+ purpose.textContent = "Idea Board optional"; -+ message.textContent = nextMessage; -+ action.textContent = "Open Game Hub"; -+ return; -+ } -+ -+ if (state === "error") { -+ name.textContent = "Unavailable"; -+ purpose.textContent = "Game Hub selected game could not be read"; -+ message.textContent = nextMessage; -+ action.textContent = "Open Game Hub"; -+ return; -+ } -+ -+ name.textContent = "No game selected"; -+ purpose.textContent = "Game Hub owns game selection"; -+ message.textContent = "Select or create a game in Game Hub before using this toolbox page."; -+ action.textContent = "Select or Create in Game Hub"; -+} -+ -+export function refreshToolboxStatusBar() { -+ const bar = ensureStatusBar(); -+ let selectedGame = null; -+ let state = "missing"; -+ let message = ""; -+ -+ try { -+ selectedGame = readSelectedGame(); -+ state = selectedGame ? "active" : "missing"; -+ } catch (error) { -+ state = "error"; -+ message = error instanceof Error ? error.message : String(error || "Game Hub selected game is unavailable."); -+ } -+ -+ publishSelectedGameContext(selectedGame, state); -+ renderSelectedGame(bar, selectedGame, state, message); -+ placeStatusBar(bar); -+} -+ -+function installEventListeners() { -+ if (listenersInstalled) { -+ return; -+ } -+ listenersInstalled = true; -+ document.addEventListener("click", scheduleToolMessageRefresh, true); -+ document.addEventListener("submit", scheduleToolMessageRefresh, true); -+ document.addEventListener("change", scheduleToolMessageRefresh, true); -+ window.addEventListener("gamefoundry:toolbox-selected-game-changed", refreshToolboxStatusBar); -+ window.addEventListener("gamefoundry:data-changed", refreshToolboxStatusBar); -+} -+ -+export function mountToolboxStatusBar(options = {}) { -+ mountOptions = { -+ ...mountOptions, -+ ...options, -+ }; -+ ensureStatusBar(); -+ observeToolMessages(); -+ installEventListeners(); -+ refreshToolboxStatusBar(); -+} -diff --git a/toolbox/assets/assets-upload-worker.js b/assets/toolbox/assets/js/assets-upload-worker.js -similarity index 100% -rename from toolbox/assets/assets-upload-worker.js -rename to assets/toolbox/assets/js/assets-upload-worker.js -diff --git a/toolbox/assets/assets.js b/assets/toolbox/assets/js/index.js -similarity index 99% -rename from toolbox/assets/assets.js -rename to assets/toolbox/assets/js/index.js -index 712917f48..d09e88a81 100644 ---- a/toolbox/assets/assets.js -+++ b/assets/toolbox/assets/js/index.js -@@ -2,8 +2,8 @@ import { - ASSET_CATALOG_TYPES, - ASSET_USAGE_OPTIONS, - createAssetToolApiRepository --} from "./assets-api-client.js"; --import { getSessionCurrent } from "../../src/api/session-api-client.js"; -+} from "../../../js/shared/assets-api-client.js"; -+import { getSessionCurrent } from "../../../../src/api/session-api-client.js"; - - const repository = createAssetToolApiRepository(); - const params = new URLSearchParams(window.location.search); -diff --git a/toolbox/colors/colors.js b/assets/toolbox/colors/js/index.js -similarity index 97% -rename from toolbox/colors/colors.js -rename to assets/toolbox/colors/js/index.js -index 99efed4f5..dcc0631e5 100644 ---- a/toolbox/colors/colors.js -+++ b/assets/toolbox/colors/js/index.js -@@ -1,20 +1,36 @@ - import { -- CURATED_PALETTE_COLLECTIONS, -- NUMERIC_VARIANT_COUNTS, -- PALETTE_SOURCE_USER, -- PALETTE_GENERATOR_DEFAULTS, -- PALETTE_TOOL_KEY, -- PALETTE_VARIANTS, -- PALETTE_WORKSPACE_PATH, -- PICKER_PREVIEW_DEFAULTS, -- PICKER_PREVIEW_SORT_OPTIONS, -- SIZE_OPTIONS, -- SORT_OPTIONS, -- SUGGESTED_TAGS, -- createGameWorkspacePaletteApiRepository, -- normalizePaletteSwatchInput, -- validatePaletteSwatchInput --} from "./palette-api-client.js"; -+ callServerToolFunction, -+ createServerRepositoryClient, -+ readServerToolConstants, -+ requireServerConstant, -+} from "../../../../src/api/server-api-client.js"; -+ -+const constants = readServerToolConstants("palette"); -+ -+export const PALETTE_SOURCE_USER = requireServerConstant(constants, "PALETTE_SOURCE_USER", "palette"); -+export const PALETTE_TOOL_KEY = requireServerConstant(constants, "PALETTE_TOOL_KEY", "palette"); -+export const PALETTE_WORKSPACE_PATH = requireServerConstant(constants, "PALETTE_WORKSPACE_PATH", "palette"); -+export const CURATED_PALETTE_COLLECTIONS = Object.freeze(requireServerConstant(constants, "CURATED_PALETTE_COLLECTIONS", "palette")); -+export const NUMERIC_VARIANT_COUNTS = Object.freeze(requireServerConstant(constants, "NUMERIC_VARIANT_COUNTS", "palette")); -+export const PALETTE_GENERATOR_DEFAULTS = Object.freeze(requireServerConstant(constants, "PALETTE_GENERATOR_DEFAULTS", "palette")); -+export const PALETTE_VARIANTS = Object.freeze(requireServerConstant(constants, "PALETTE_VARIANTS", "palette")); -+export const PICKER_PREVIEW_DEFAULTS = Object.freeze(requireServerConstant(constants, "PICKER_PREVIEW_DEFAULTS", "palette")); -+export const PICKER_PREVIEW_SORT_OPTIONS = Object.freeze(requireServerConstant(constants, "PICKER_PREVIEW_SORT_OPTIONS", "palette")); -+export const SIZE_OPTIONS = Object.freeze(requireServerConstant(constants, "SIZE_OPTIONS", "palette")); -+export const SORT_OPTIONS = Object.freeze(requireServerConstant(constants, "SORT_OPTIONS", "palette")); -+export const SUGGESTED_TAGS = Object.freeze(requireServerConstant(constants, "SUGGESTED_TAGS", "palette")); -+ -+export function createGameWorkspacePaletteApiRepository(options = {}) { -+ return createServerRepositoryClient("palette", options); -+} -+ -+export function normalizePaletteSwatchInput(input) { -+ return callServerToolFunction("palette", "normalizePaletteSwatchInput", input); -+} -+ -+export function validatePaletteSwatchInput(input, existingSwatches, options) { -+ return callServerToolFunction("palette", "validatePaletteSwatchInput", input, existingSwatches, options); -+} - - const params = new URLSearchParams(window.location.search); - -diff --git a/toolbox/controls/controls.js b/assets/toolbox/controls/js/index.js -similarity index 99% -rename from toolbox/controls/controls.js -rename to assets/toolbox/controls/js/index.js -index c3355a91e..6a648b878 100644 ---- a/toolbox/controls/controls.js -+++ b/assets/toolbox/controls/js/index.js -@@ -5,11 +5,11 @@ import { - GAME_CONTROL_NORMALIZED_INPUTS, - NORMALIZED_USAGE_LABELS, - createControlsToolApiRepository, --} from "./controls-api-client.js"; -+} from "../../../js/shared/controls-api-client.js"; - import { - normalizeNormalizedInput, - normalizedInputOptions, --} from "../../src/engine/input/NormalizedInputRegistry.js"; -+} from "../../../../src/engine/input/NormalizedInputRegistry.js"; - - const GAME_CONTROL_NORMALIZED_INPUT_IDS = new Set(GAME_CONTROL_NORMALIZED_INPUTS); - -diff --git a/toolbox/events/events.js b/assets/toolbox/events/js/index.js -similarity index 99% -rename from toolbox/events/events.js -rename to assets/toolbox/events/js/index.js -index 80df22928..837198395 100644 ---- a/toolbox/events/events.js -+++ b/assets/toolbox/events/js/index.js -@@ -1,7 +1,7 @@ - import { - requireServerApiData, - safeRequestServerApi, --} from "../../src/api/server-api-client.js"; -+} from "../../../../src/api/server-api-client.js"; - - const NEW_ROW_KEY = "__new__"; - const TABLE_COLSPAN = 5; -diff --git a/toolbox/game-configuration/game-configuration.js b/assets/toolbox/game-configuration/js/index.js -similarity index 92% -rename from toolbox/game-configuration/game-configuration.js -rename to assets/toolbox/game-configuration/js/index.js -index b907ca893..d94f3ced2 100644 ---- a/toolbox/game-configuration/game-configuration.js -+++ b/assets/toolbox/game-configuration/js/index.js -@@ -1,7 +1,17 @@ - import { -- GAME_CONFIGURATION_SECTIONS, -- createGameConfigurationApiRepository --} from "./game-configuration-api-client.js"; -+ createServerRepositoryClient, -+ readServerToolConstants, -+ requireServerConstant, -+} from "../../../../src/api/server-api-client.js"; -+ -+const constants = readServerToolConstants("game-configuration"); -+ -+export const GAME_CONFIGURATION_SECTIONS = Object.freeze(requireServerConstant(constants, "GAME_CONFIGURATION_SECTIONS", "game-configuration")); -+export const GAME_CONFIGURATION_PLAYER_MODES = Object.freeze(requireServerConstant(constants, "GAME_CONFIGURATION_PLAYER_MODES", "game-configuration")); -+ -+export function createGameConfigurationApiRepository(options = {}) { -+ return createServerRepositoryClient("game-configuration", options); -+} - - const repository = createGameConfigurationApiRepository(); - const params = new URLSearchParams(window.location.search); -diff --git a/toolbox/game-design/game-design.js b/assets/toolbox/game-design/js/index.js -similarity index 92% -rename from toolbox/game-design/game-design.js -rename to assets/toolbox/game-design/js/index.js -index 51466ecff..af61960cd 100644 ---- a/toolbox/game-design/game-design.js -+++ b/assets/toolbox/game-design/js/index.js -@@ -1,10 +1,19 @@ - import { -- GAME_DESIGN_GAME_TYPES, -- GAME_DESIGN_GENRES, -- GAME_DESIGN_PLAYER_MODES, -- GAME_DESIGN_PLAY_STYLES, -- createGameDesignApiRepository --} from "./game-design-api-client.js"; -+ createServerRepositoryClient, -+ readServerToolConstants, -+ requireServerConstant, -+} from "../../../../src/api/server-api-client.js"; -+ -+const constants = readServerToolConstants("game-design"); -+ -+export const GAME_DESIGN_GAME_TYPES = Object.freeze(requireServerConstant(constants, "GAME_DESIGN_GAME_TYPES", "game-design")); -+export const GAME_DESIGN_GENRES = Object.freeze(requireServerConstant(constants, "GAME_DESIGN_GENRES", "game-design")); -+export const GAME_DESIGN_PLAYER_MODES = Object.freeze(requireServerConstant(constants, "GAME_DESIGN_PLAYER_MODES", "game-design")); -+export const GAME_DESIGN_PLAY_STYLES = Object.freeze(requireServerConstant(constants, "GAME_DESIGN_PLAY_STYLES", "game-design")); -+ -+export function createGameDesignApiRepository(options = {}) { -+ return createServerRepositoryClient("game-design", options); -+} - - const repository = createGameDesignApiRepository(); - const params = new URLSearchParams(window.location.search); -diff --git a/toolbox/game-journey/game-journey.js b/assets/toolbox/game-journey/js/index.js -similarity index 98% -rename from toolbox/game-journey/game-journey.js -rename to assets/toolbox/game-journey/js/index.js -index c2b2f876c..085df839c 100644 ---- a/toolbox/game-journey/game-journey.js -+++ b/assets/toolbox/game-journey/js/index.js -@@ -6,11 +6,11 @@ import { - GAME_JOURNEY_SUGGESTED_TOOLS, - createGameJourneyApiRepository, - readGameJourneyCompletionMetrics, --} from "./game-journey-api-client.js"; -+} from "../../../js/shared/game-journey-api-client.js"; - import { - getActiveToolRegistry, - getToolRegistryApiDiagnostic, --} from "../tool-registry-api-client.js"; -+} from "../../../../toolbox/tool-registry-api-client.js"; - - const repository = createGameJourneyApiRepository(); - const registryDiagnostic = getToolRegistryApiDiagnostic(); -@@ -1161,7 +1161,7 @@ function renderRecommendedTargets() { - table.setAttribute("aria-label", "Game Journey recommended planning targets"); - const head = createElement("thead"); - const headRow = createElement("tr"); -- ["Target", "Section", "Suggested"].forEach((heading) => { -+ ["Target", "Section", "Count"].forEach((heading) => { - const cell = createElement("th", { text: heading }); - cell.scope = "col"; - headRow.append(cell); -@@ -1169,16 +1169,22 @@ function renderRecommendedTargets() { - head.append(headRow); - const body = createElement("tbody"); - targets.forEach((target) => { -+ const targetCount = recommendedTargetValues.get(target.key) ?? target.suggestedCount; - const row = createElement("tr"); - row.dataset.journeyRecommendedTarget = target.key; -- const labelCell = createElement("td", { text: target.label }); -+ const labelCell = createElement("td"); -+ const label = createElement("span", { text: target.label }); -+ const countPreview = createElement("span", { text: ` [${targetCount}]` }); -+ countPreview.dataset.journeyTargetCountPreview = target.key; -+ labelCell.append(label, countPreview); - const sectionCell = createElement("td", { text: target.sectionName }); - const input = document.createElement("input"); - input.type = "number"; -+ input.inputMode = "numeric"; - input.min = "0"; - input.step = "1"; -- input.value = String(recommendedTargetValues.get(target.key) ?? target.suggestedCount); -- input.setAttribute("aria-label", `${target.label} suggested target`); -+ input.value = String(targetCount); -+ input.setAttribute("aria-label", `${target.label} count`); - input.dataset.journeyTargetInput = target.key; - const inputCell = createElement("td"); - inputCell.append(input); -@@ -1773,6 +1779,10 @@ recommendedTargets?.addEventListener("input", (event) => { - const savedValue = normalizeTargetCount(updated.suggestedCount); - recommendedTargetValues.set(target.key, savedValue); - input.value = String(savedValue); -+ const preview = recommendedTargets.querySelector(`[data-journey-target-count-preview='${target.key}']`); -+ if (preview) { -+ preview.textContent = ` [${savedValue}]`; -+ } - if (recommendedTargetStatus) { - recommendedTargetStatus.textContent = `Saved ${target.label} target at ${savedValue}.`; - } -diff --git a/toolbox/idea-board/index.js b/assets/toolbox/idea-board/js/index.js -similarity index 93% -rename from toolbox/idea-board/index.js -rename to assets/toolbox/idea-board/js/index.js -index d1e339336..47a5ed91e 100644 ---- a/toolbox/idea-board/index.js -+++ b/assets/toolbox/idea-board/js/index.js -@@ -1,9 +1,12 @@ --import { createServerRepositoryClient } from "../../src/api/server-api-client.js"; -+import { createServerRepositoryClient } from "../../../../src/api/server-api-client.js"; -+import { getSessionCurrent } from "../../../../src/api/session-api-client.js"; - --const statusOptions = Object.freeze(["New", "Exploring", "Refining", "Ready", "Project", "Archived"]); -+const editableStatusOptions = Object.freeze(["New", "Exploring", "Refining", "Ready"]); -+const filterStatusOptions = Object.freeze(["New", "Exploring", "Refining", "Ready", "Project", "Archived"]); - const defaultVisibleStatuses = Object.freeze(["New", "Exploring", "Refining", "Ready", "Project"]); - const userId = "user-1"; - const gameHubRoute = "toolbox/game-hub/index.html"; -+const signInRoute = "account/sign-in.html"; - let gameHubRepository = null; - - const ideaTable = [ -@@ -121,7 +124,7 @@ function visibleIdeas() { - } - - function previousStatusForRestore(record) { -- return statusOptions.includes(record.previousStatus) && record.previousStatus !== "Archived" -+ return filterStatusOptions.includes(record.previousStatus) && record.previousStatus !== "Archived" - ? record.previousStatus - : "Refining"; - } -@@ -166,7 +169,7 @@ function renderStatusFilter(root) { - const options = root.querySelector("[data-idea-board-status-options]"); - if (!options) return; - options.replaceChildren(); -- for (const status of statusOptions) { -+ for (const status of filterStatusOptions) { - const label = document.createElement("label"); - label.className = "idea-board-show-filter__option"; - const input = document.createElement("input"); -@@ -193,7 +196,7 @@ function statusSelect(value) { - const select = document.createElement("select"); - select.setAttribute("aria-label", "Idea status"); - select.dataset.ideaBoardIdeaStatusInput = "true"; -- for (const optionValue of statusOptions) { -+ for (const optionValue of editableStatusOptions) { - const option = document.createElement("option"); - option.value = optionValue; - option.textContent = optionValue; -@@ -229,7 +232,6 @@ function renderIdeaInputRow(tbody, record = null) { - const statusCell = document.createElement("td"); - statusCell.append(statusSelect(record?.status || "New")); - row.append(statusCell); -- row.append(cell(record?.updated || today())); - row.append(cell(record ? noteCountLabel(record.ideaId) : "0 Notes")); - - const actions = document.createElement("td"); -@@ -272,7 +274,6 @@ function renderIdeaRow(tbody, record) { - row.append(idea); - row.append(cell(record.pitch)); - row.append(cell(record.status)); -- row.append(cell(record.updated)); - - const notes = document.createElement("td"); - const notesCount = document.createElement("span"); -@@ -351,7 +352,7 @@ function renderExpandedNotesRow(tbody, record) { - row.dataset.ideaBoardExpandedRow = record.ideaId; - - const content = document.createElement("td"); -- content.colSpan = 6; -+ content.colSpan = 5; - - const childSurface = document.createElement("div"); - childSurface.className = "idea-board-notes-child-surface"; -@@ -398,7 +399,7 @@ function renderAddIdeaRow(tbody) { - const row = document.createElement("tr"); - row.dataset.ideaBoardAddIdeaRow = "true"; - const actions = document.createElement("td"); -- actions.colSpan = 6; -+ actions.colSpan = 5; - const addIdea = actionButton("Add Idea", "add", "ideaBoardIdeaAction", "primary"); - addIdea.dataset.ideaBoardAddIdea = "true"; - actions.append(addIdea); -@@ -561,6 +562,28 @@ function gameHubUrl(record) { - return `${gameHubRoute}${suffix}`; - } - -+function signInUrl() { -+ return new URL(signInRoute, document.baseURI || window.location.href).href; -+} -+ -+function currentSessionState() { -+ try { -+ const session = getSessionCurrent(); -+ return { -+ apiAvailable: true, -+ authenticated: Boolean(session?.authenticated && session.userKey), -+ session, -+ }; -+ } catch (error) { -+ console.warn("Idea Board could not verify the current session.", error instanceof Error ? error.message : String(error || "")); -+ return { -+ apiAvailable: false, -+ authenticated: false, -+ session: null, -+ }; -+ } -+} -+ - function createProject(root, ideaId) { - const record = ideaRecord(ideaId); - if (!record) { -@@ -571,6 +594,16 @@ function createProject(root, ideaId) { - updateStatus(root, "Set this idea to Ready before creating a project."); - return; - } -+ const sessionState = currentSessionState(); -+ if (!sessionState.apiAvailable) { -+ updateStatus(root, "Sign-in status could not be verified. Try again shortly."); -+ return; -+ } -+ if (!sessionState.authenticated) { -+ updateStatus(root, "Sign in to create a Game Hub project."); -+ window.location.href = signInUrl(); -+ return; -+ } - const repository = gameHubProjectRepository(); - const project = repository.createGame({ - name: record.idea, -@@ -723,7 +756,7 @@ function handleNoteAction(root, actionControl) { - - function handleFilterAction(root, actionControl) { - if (actionControl.matches("[data-idea-board-filter-select-all]")) { -- state.visibleStatuses = new Set(statusOptions); -+ state.visibleStatuses = new Set(filterStatusOptions); - updateStatus(root, "Showing all statuses."); - } else if (actionControl.matches("[data-idea-board-filter-clear-all]")) { - state.visibleStatuses = new Set(); -diff --git a/toolbox/objects/objects.js b/assets/toolbox/objects/js/index.js -similarity index 96% -rename from toolbox/objects/objects.js -rename to assets/toolbox/objects/js/index.js -index bd600c533..0f332df58 100644 ---- a/toolbox/objects/objects.js -+++ b/assets/toolbox/objects/js/index.js -@@ -1,16 +1,49 @@ -+import { -+ createServerRepositoryClient, -+ readServerToolConstants, -+ requireServerConstant, -+} from "../../../../src/api/server-api-client.js"; - import { - OBJECT_MODEL_TRAIT_LIST, - getObjectModelTrait, - getObjectModelType, - validateObjectDefinition, --} from "../../src/engine/object-model/index.js"; --import { createAssetToolApiRepository } from "../assets/assets-api-client.js"; --import { -- CAPABILITY_LABELS, -- OBJECT_TYPE_TEMPLATES, -- STARTER_OBJECTS, -- createObjectsToolApiRepository, --} from "./objects-api-client.js"; -+} from "../../../../src/engine/object-model/index.js"; -+import { createAssetToolApiRepository } from "../../../js/shared/assets-api-client.js"; -+ -+const constants = readServerToolConstants("objects"); -+ -+function freezeTemplate(template = {}) { -+ return Object.freeze({ -+ ...template, -+ capabilities: Object.freeze(Array.isArray(template.capabilities) ? [...template.capabilities] : []), -+ }); -+} -+ -+function freezeStarterObject(object = {}) { -+ return Object.freeze({ -+ ...object, -+ render: Object.freeze({ ...(object.render || {}) }), -+ }); -+} -+ -+export const CAPABILITY_LABELS = Object.freeze( -+ { ...requireServerConstant(constants, "CAPABILITY_LABELS", "objects") }, -+); -+ -+export const OBJECT_TYPE_TEMPLATES = Object.freeze( -+ requireServerConstant(constants, "OBJECT_TYPE_TEMPLATES", "objects").map(freezeTemplate), -+); -+ -+export const OBJECTS_TOOL_TABLES = Object.freeze(requireServerConstant(constants, "OBJECTS_TOOL_TABLES", "objects")); -+ -+export const STARTER_OBJECTS = Object.freeze( -+ requireServerConstant(constants, "STARTER_OBJECTS", "objects").map(freezeStarterObject), -+); -+ -+export function createObjectsToolApiRepository(options = {}) { -+ return createServerRepositoryClient("objects", options); -+} - - let assetRepository = createAssetToolApiRepository(); - let objectsRepository = createObjectsToolApiRepository(); -diff --git a/toolbox/tags/tags.js b/assets/toolbox/tags/js/index.js -similarity index 95% -rename from toolbox/tags/tags.js -rename to assets/toolbox/tags/js/index.js -index c43c6fc89..118923da9 100644 ---- a/toolbox/tags/tags.js -+++ b/assets/toolbox/tags/js/index.js -@@ -1,4 +1,16 @@ --import { createTagsToolApiRepository } from "./tags-api-client.js"; -+import { -+ createServerRepositoryClient, -+ readServerToolConstants, -+ requireServerConstant, -+} from "../../../../src/api/server-api-client.js"; -+ -+const constants = readServerToolConstants("tags"); -+ -+export const TAGS_TOOL_TABLES = Object.freeze(requireServerConstant(constants, "TAGS_TOOL_TABLES", "tags")); -+ -+export function createTagsToolApiRepository(options = {}) { -+ return createServerRepositoryClient("tags", options); -+} - - const repository = createTagsToolApiRepository(); - -diff --git a/toolbox/text-to-speech/text2speech.js b/assets/toolbox/text-to-speech/js/index.js -similarity index 99% -rename from toolbox/text-to-speech/text2speech.js -rename to assets/toolbox/text-to-speech/js/index.js -index 081fa25b2..77e090f4e 100644 ---- a/toolbox/text-to-speech/text2speech.js -+++ b/assets/toolbox/text-to-speech/js/index.js -@@ -1,7 +1,7 @@ - import { - textToSpeechLanguageOptionsFromVoices, - TextToSpeechEngine, --} from "../../src/engine/audio/TextToSpeechEngine.js"; -+} from "../../../../src/engine/audio/TextToSpeechEngine.js"; - import { - TEXT_TO_SPEECH_AGE_FILTER_OPTIONS, - TEXT_TO_SPEECH_DEFAULTS, -@@ -9,11 +9,11 @@ import { - TEXT_TO_SPEECH_LANGUAGE_OPTIONS, - TEXT_TO_SPEECH_RANGE_DEFAULTS, - TEXT_TO_SPEECH_SSML_LIKE_PRESET_OPTIONS --} from "../../src/engine/audio/TextToSpeechDefaults.js"; -+} from "../../../../src/engine/audio/TextToSpeechDefaults.js"; - import { - readSavedTextToSpeechProfiles, - writeSavedTextToSpeechProfiles, --} from "./tts-profile-store.js"; -+} from "../../../js/shared/tts-profile-store.js"; - - const TTS_OWNERSHIP = Object.freeze({ - DESIGN: "Design", -diff --git a/docs_build/dev/BUILD_PR.md b/docs_build/dev/BUILD_PR.md -index 20d0cb9f0..dd5b4d28f 100644 ---- a/docs_build/dev/BUILD_PR.md -+++ b/docs_build/dev/BUILD_PR.md -@@ -1,27 +1,63 @@ --# BUILD_PR: Schema location correction -+# PR_26175_ALFA_008-game-hub-feature-matrix - --## Codex task --Move the schema contract plan from root-level schema files to `src/shared/schemas/`. -+## Purpose -+Audit the current Game Hub workflow and publish a feature matrix that maps implemented creator-facing behavior to code and Playwright evidence. - --## Required changes --1. Remove any planned or newly-added root-level `*.schema.json` files. --2. Add schema contracts under `src/shared/schemas/` only. --3. Put reusable manifest schemas directly under `src/shared/schemas/`. --4. Put individual tool payload schemas under `src/shared/schemas/tools/`. --5. Update any docs or references to point to the new schema paths. -+## Source Of Truth -+This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_008-game-hub-feature-matrix`. - --## Do not --- Do not write broad validation utilities as the primary deliverable. --- Do not change sample payloads. --- Do not unlock or mutate samples. --- Do not create schema files at repository root. --- Do not modify start_of_day folders. -+## Exact Scope -+- Produce a Game Hub feature matrix only. -+- Audit Game Hub table workflow, selected/open game behavior, create/edit/delete actions, child tables, guest save gating, empty/error states, Theme V2 layout, and targeted Game Hub coverage. -+- Use current `main` behavior as evidence. -+- Preserve Game Hub UI/product behavior. -+- Preserve API/service/repository contracts. -+- Preserve previous ALFA Game Hub cleanup and create-validation behavior. -+- Do not implement product/UI changes unless validation exposes a requirement-critical defect. - --## Validation command --Search for misplaced schemas: -+## Exact Targets -+- `docs_build/dev/BUILD_PR.md` -+- `docs_build/dev/reports/PR_26175_ALFA_008-game-hub-feature-matrix_report.md` -+- `docs_build/dev/reports/PR_26175_ALFA_008-game-hub-feature-matrix_validation-lane.md` -+- `docs_build/dev/reports/PR_26175_ALFA_008-game-hub-feature-matrix_requirements-checklist.md` -+- `docs_build/dev/reports/codex_review.diff` -+- `docs_build/dev/reports/codex_changed_files.txt` -+ -+## Evidence Sources -+- `toolbox/game-hub/index.html` -+- `toolbox/project-workspace/index.html` -+- `toolbox/game-hub/game-hub.js` -+- `toolbox/game-hub/game-hub-api-client.js` -+- `src/dev-runtime/persistence/tool-repositories/game-workspace-mock-repository.js` -+- `tests/playwright/tools/GameHubMockRepository.spec.mjs` -+ -+## Out Of Scope -+- No Game Hub product or UI changes. -+- No Game Journey changes. -+- No shared toolbox status bar changes. -+- No browser-owned product data as source of truth. -+- No API/service/repository contract changes. -+- No inline styles, style blocks, or page-local CSS. -+- No engine core changes. -+- No `start_of_day` folder changes. -+- No ALFA_007 work. -+ -+## Validation -+Run targeted Game Hub validation: - - ```powershell --Get-ChildItem -Path . -Filter *.schema.json -Recurse | Select-Object FullName -+npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs --workers=1 - ``` - --Expected result: all schema files are under `src\shared\schemas\`. -+Also verify changed docs/reports do not introduce inline styles or style blocks: -+ -+```powershell -+rg -n "<[s]tyle|[s]tyle=" docs_build/dev/BUILD_PR.md docs_build/dev/reports/PR_26175_ALFA_008-game-hub-feature-matrix_report.md docs_build/dev/reports/PR_26175_ALFA_008-game-hub-feature-matrix_validation-lane.md docs_build/dev/reports/PR_26175_ALFA_008-game-hub-feature-matrix_requirements-checklist.md -+``` -+ -+## Artifact -+Create repo-structured delta ZIP: -+ -+```text -+tmp/PR_26175_ALFA_008-game-hub-feature-matrix_delta.zip -+``` -diff --git a/docs_build/dev/ProjectInstructions/PROJECT_INSTRUCTIONS.md b/docs_build/dev/ProjectInstructions/PROJECT_INSTRUCTIONS.md -index 40db33c44..3314b3dea 100644 ---- a/docs_build/dev/ProjectInstructions/PROJECT_INSTRUCTIONS.md -+++ b/docs_build/dev/ProjectInstructions/PROJECT_INSTRUCTIONS.md -@@ -44,3 +44,16 @@ OWNER follows the same safety rules: - - One active OWNER assignment at a time. - - OWNER may override team locks, but may not silently delete, rewrite, or remove protected instructions. - - OWNER override must be explicitly documented. -+ -+## Four-Team Ownership Alignment -+ -+The single authoritative four-team ownership definition is: -+ -+`docs_build/dev/ProjectInstructions/team_assignments/team_ownership.md` -+ -+Use the `Current Four-Team Ownership Model` section there for team ownership, assignment routing, and cross-team scope checks. -+ -+Rules: -+- Teams pull backlog items only from their ownership area unless OWNER explicitly reassigns or splits the work. -+- Cross-team work requires OWNER approval and must identify the owning team for each PR. -+- Team start commands must remain aligned with this ownership model. -diff --git a/docs_build/dev/ProjectInstructions/TEAM_START_COMMANDS.md b/docs_build/dev/ProjectInstructions/TEAM_START_COMMANDS.md -index 35279e8f1..12f74cb1f 100644 ---- a/docs_build/dev/ProjectInstructions/TEAM_START_COMMANDS.md -+++ b/docs_build/dev/ProjectInstructions/TEAM_START_COMMANDS.md -@@ -66,6 +66,30 @@ Create one Team Charlie branch for the selected assignment. - Work only that assignment. - ``` - -+## Start Team Delta -+ -+Ready-to-copy command: -+ -+```text -+OWNER override approved: Start Team Delta from the ProjectInstructions release gate. -+ -+Read docs_build/dev/ProjectInstructions/README.txt first. -+Read docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md. -+Read docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md. -+Read docs_build/dev/ProjectInstructions/team_assignments/team_ownership.md. -+ -+Pull one [ ] item for Team Delta from BACKLOG_MASTER.md. -+Stop if Team Delta already has an active branch. -+Stop if Team Delta already has an active assignment. -+Stop if no [ ] Team Delta backlog item is available. -+Stop if the selected item is outside Team Delta ownership. -+ -+Change the selected backlog item from [ ] to [.]. -+Add the selected assignment under Team Delta in TEAM_ASSIGNMENTS.md. -+Create one Team Delta branch for the selected assignment. -+Work only that assignment. -+``` -+ - ## Day Work / EOD Merge Reminder - - Ready-to-copy reminder: -diff --git a/docs_build/dev/ProjectInstructions/addendums/multi_team.md b/docs_build/dev/ProjectInstructions/addendums/multi_team.md -index 4b45257b0..3d603aff9 100644 ---- a/docs_build/dev/ProjectInstructions/addendums/multi_team.md -+++ b/docs_build/dev/ProjectInstructions/addendums/multi_team.md -@@ -1,5 +1,18 @@ - # Multi-Team Codex Execution Governance - -+## Four Active Delivery Teams -+ -+The single authoritative four-team ownership definition is: -+ -+`docs_build/dev/ProjectInstructions/team_assignments/team_ownership.md` -+ -+Use the `Current Four-Team Ownership Model` section there for active delivery team ownership. -+ -+Rules: -+- Team work must stay inside the owning team's area. -+- Cross-team work requires OWNER approval and must identify the correct owning team for each PR. -+- Team start commands must use the current ownership model before pulling a backlog item. -+ - ## All-Team Preferred Codex Execution Method - - Preferred execution model: -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/ProjectInstructions/addendums/team_start_and_release.md b/docs_build/dev/ProjectInstructions/addendums/team_start_and_release.md -index 3f85bbf25..e1018c155 100644 ---- a/docs_build/dev/ProjectInstructions/addendums/team_start_and_release.md -+++ b/docs_build/dev/ProjectInstructions/addendums/team_start_and_release.md -@@ -20,6 +20,17 @@ Before a team starts, validate: - - assigned team uses NATO phonetic naming - - work remains with the assigned team until complete or OWNER reassignment - -+## Current Four-Team Start Set -+ -+The current active delivery teams for backlog start commands are: -+ -+- Team Alfa -+- Team Bravo -+- Team Charlie -+- Team Delta -+ -+Each team start must confirm the selected backlog item is inside that team's ownership area. -+ - ## Assignment Flow - - For backlog-driven work: -diff --git a/docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md b/docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md -index 09cc34579..0b8942261 100644 ---- a/docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md -+++ b/docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md -@@ -4,17 +4,17 @@ - - ### Idea - --0% Complete — Dream, brainstorm, and explore early game concepts -+33% Complete — Dream, brainstorm, and explore early game concepts - --- [ ] Alfa - Idea Board -+- [x] Alfa - Idea Board - - [ ] Alfa - Game Concept Notes - - [ ] Alfa - Creator Learning - - ### Design - --0% Complete — Shape the game's story, structure, and systems -+17% Complete — Shape the game's story, structure, and systems - --- [ ] Alfa - Game Hub -+- [x] Alfa - Game Hub - - [ ] Alfa - Game Design - - [ ] Alfa - Game Configuration - - [ ] Alfa - Game Crew -@@ -159,3 +159,45 @@ - - Open PR, draft PR, local branch, and remote branch review requirements verified. - - Recommendation-only hygiene audit process verified. - - Branch deletion and PR closure remain prohibited without explicit owner approval. -+ -+## Four-Team Backlog Alignment -+ -+### Team Alfa -+ -+- [ ] Alfa - Game Hub polish -+- [ ] Alfa - Game Journey completion tracking -+- [ ] Alfa - Journey progress calculations -+- [ ] Alfa - Creator onboarding flow -+- [ ] Alfa - Game Hub image integration -+ -+### Team Bravo -+ -+- [ ] Bravo - Audio tool improvements -+- [ ] Bravo - Audio Effects tool -+- [ ] Bravo - Messages tool -+- [ ] Bravo - Emotion Profiles -+- [ ] Bravo - TTS Profiles -+- [ ] Bravo - Asset Browser enhancements -+- [ ] Bravo - Vector Art improvements -+- [ ] Bravo - MIDI Studio improvements -+ -+### Team Charlie -+ -+- [ ] Charlie - Guardrail hardening -+- [ ] Charlie - Browser validation hardening -+- [ ] Charlie - Remaining test relocation audit -+- [ ] Charlie - Compliance baseline freeze -+- [ ] Charlie - System Health improvements -+- [ ] Charlie - Infrastructure dashboard -+- [ ] Charlie - Environment validation -+ -+### Team Delta -+ -+- [ ] Delta - Shared JS consolidation -+- [ ] Delta - API client consolidation -+- [ ] Delta - Runtime performance audit -+- [ ] Delta - Engine test coverage improvements -+- [ ] Delta - Event system audit -+- [ ] Delta - Controls runtime framework audit -+- [ ] Delta - Object runtime framework audit -+- [ ] Delta - World runtime framework audit -diff --git a/docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md b/docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md -index 9a3c515f7..acefbba8e 100644 ---- a/docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md -+++ b/docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md -@@ -57,6 +57,14 @@ Active assignment: none. - - Active branch: none. - -+## Team Delta -+ -+Status: Available -+ -+Active assignment: none. -+ -+Active branch: none. -+ - ## Team OWNER - - Status: Available -diff --git a/docs_build/dev/ProjectInstructions/team_assignments/team_ownership.md b/docs_build/dev/ProjectInstructions/team_assignments/team_ownership.md -index baa1598b6..865cca551 100644 ---- a/docs_build/dev/ProjectInstructions/team_assignments/team_ownership.md -+++ b/docs_build/dev/ProjectInstructions/team_assignments/team_ownership.md -@@ -1,43 +1,56 @@ - # Team Ownership Governance - --## OWNER -+## Current Four-Team Ownership Model - --- Architecture --- Product Direction --- Cross-Team Governance --- Final Approval -+This section is the current OWNER-approved active ownership alignment. - - ## Team Alfa - --- Idea Board - - Game Hub - - Game Journey --- Design --- Objects --- Worlds --- Interface --- Controls --- Rules --- Progression -+- Idea Board -+- Creator workflow -+- Creator onboarding -+- UX flow - - ## Team Bravo - --- Graphics - - Audio -+- Audio Effects - - Messages --- TTS --- MIDI --- Publishing --- Community --- Marketplace -+- Emotion Profiles -+- TTS Profiles -+- Asset Browser -+- Vector Art -+- MIDI Studio -+- Creator content tools - - ## Team Charlie - --- Governance -+- Repository compliance -+- Validation - - Infrastructure --- Operations --- Diagnostics -+- Storage -+- Environment management - - System Health -+- Operations -+ -+## Team Delta -+ -+- Engine -+- Runtime -+- Shared JS -+- API clients -+- Event systems -+- Performance -+- Technical debt remediation -+- Runtime test coverage -+ -+## Current Four-Team Rule -+ -+Alfa, Bravo, Charlie, and Delta are the four active delivery teams for backlog ownership and team start routing. -+ -+Each team may pull only from its ownership area unless OWNER explicitly reassigns, splits, or approves cross-team work. - - ## Rule - -diff --git a/docs_build/dev/reports/PR_26172_CHARLIE_001-repository-compliance-audit.md b/docs_build/dev/reports/PR_26172_CHARLIE_001-repository-compliance-audit.md -new file mode 100644 -index 000000000..ff09afb85 ---- /dev/null -+++ b/docs_build/dev/reports/PR_26172_CHARLIE_001-repository-compliance-audit.md -@@ -0,0 +1,236 @@ -+# PR_26172_CHARLIE_001 Repository Compliance Audit ++++ b/docs_build/dev/reports/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review.md +@@ -0,0 +1,200 @@ ++# PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review + +## Scope + -+Audit repository compliance against: -+ -+- PR_034 Canonical Repository Structure -+- PR_035 Test Structure Standardization -+- PR_036 Legacy Migration Policy -+ -+Reviewed areas: -+ -+- `toolbox/` -+- `assets/` -+- `tests/` -+- `api/` -+- `serverside/` -+- `src/engine/` -+ -+This PR is audit-only. No executable implementation changes were made. -+ -+## Team Ownership -+ -+- TEAM token: CHARLIE -+- Ownership classification: governance / repository compliance / diagnostics -+- TEAM ownership result: PASS ++OWNER override approved: review GitHub PR #3 only. ++ ++This report reviews GitHub PR #3, `Pr/PR 26171 006 message emotion profile management`, for the Messages / Emotion Profiles area. It is report-only. No runtime code was changed, no PR was merged or closed, and no branch was deleted. ++ ++## Source Evidence ++ ++- GitHub PR: https://github.com/ToolboxAid/HTML-JavaScript-Gaming/pull/3 ++- PR state: open, non-draft, not merged ++- GitHub mergeable state: false ++- Base branch: `main` ++- Head branch: `pr/PR_26171_006-message-emotion-profile-management` ++- Base SHA: `64231546e8f6a20810d23c590a9787eed40f63a1` ++- Head SHA: `6b83cedd5196c8117ec6deb69f73e91f04080573` ++- Review date: 2026-06-24 ++- Current `main` at review start: `5415f6675d7a0f10931b83368948a83df98d8021` ++ ++## Changed Code Files Only ++ ++| File | Status | Additions | Deletions | Review Summary | ++| --- | --- | ---: | ---: | --- | ++| `src/dev-runtime/messages/messages-sqlite-service.mjs` | modified | 54 | 4 | Adds emotion-profile usage queries, reference payloads, aggregate usage counts, and a referenced-profile deactivation guard. | ++| `tests/playwright/tools/MessagesTool.spec.mjs` | modified | 37 | 0 | Adds targeted coverage for usage counts and referenced-profile deactivation rejection. | ++| `toolbox/messages/index.html` | modified | 5 | 4 | Adds a Usage column to the Emotion Profiles table and changes numeric input step precision. | ++| `toolbox/messages/messages.js` | modified | 7 | 1 | Renders usage count and reloads emotion profiles after segment reloads. | ++ ++## Full Changed File List ++ ++| File | Status | Additions | Deletions | ++| --- | --- | ---: | ---: | ++| `docs_build/dev/reports/PR_26171_006-message-emotion-profile-management-manual-validation.md` | added | 11 | 0 | ++| `docs_build/dev/reports/PR_26171_006-message-emotion-profile-management-validation.txt` | added | 30 | 0 | ++| `docs_build/dev/reports/PR_26171_006-message-emotion-profile-management.md` | added | 53 | 0 | ++| `docs_build/dev/reports/codex_changed_files.txt` | modified | 8 | 9 | ++| `docs_build/dev/reports/codex_review.diff` | modified | 352 | 305 | ++| `docs_build/pr/BUILD_PR_26171_006-message-emotion-profile-management.md` | added | 83 | 0 | ++| `docs_build/pr/BUILD_PR_26171_008-message-tts-profile-foundation.md` | added | 98 | 0 | ++| `docs_build/pr/BUILD_PR_26171_010-message-speech-preview.md` | added | 84 | 0 | ++| `docs_build/pr/BUILD_PR_26171_012-message-voice-provider-adapters.md` | added | 86 | 0 | ++| `docs_build/pr/BUILD_PR_26171_014-runtime-message-playback-foundation.md` | added | 84 | 0 | ++| `docs_build/pr/BUILD_PR_26171_016-midi-studio-roadmap-foundation.md` | added | 74 | 0 | ++| `src/dev-runtime/messages/messages-sqlite-service.mjs` | modified | 54 | 4 | ++| `tests/playwright/tools/MessagesTool.spec.mjs` | modified | 37 | 0 | ++| `toolbox/messages/index.html` | modified | 5 | 4 | ++| `toolbox/messages/messages.js` | modified | 7 | 1 | + -+## Branch Validation -+ -+| Requirement | Status | Evidence | -+| --- | --- | --- | -+| Started from latest main | PASS | `main` was pulled before branch creation; source commit `c4a495f0aa8e32d499ca64555c4a3547e6fcb298`. | -+| Worktree clean before branch | PASS | `git status --short` returned no output before branch creation. | -+| Local/origin sync before branch | PASS | `git rev-list --left-right --count HEAD...origin/main` returned `0 0`. | -+| PR branch created from main | PASS | Branch `pr/26172-CHARLIE-001-repository-compliance-audit` was created from latest `main`. | -+| Runtime/source edits avoided | PASS | Planned changed files are reports only under `docs_build/dev/reports/`. | -+ -+## Repository Area Results -+ -+| Area | Status | Findings | -+| --- | --- | --- | -+| `toolbox/` | FAIL | Active tool JavaScript remains beside tool HTML entries instead of canonical `assets/toolbox/{tool-name}/js/index.js` or shared asset roots. | -+| `assets/` | PASS | No non-compliant JS or CSS files were found in `assets/`; scanned JS/CSS lives under `assets/theme-v2/`. | -+| `tests/` | FAIL | 494 files are in non-canonical top-level test locations outside `tests/toolbox/`, `tests/engine/`, `tests/api/`, `tests/server/`, `tests/js/shared/`, or `tests/regression/`. | -+| `api/` | FAIL | Canonical root is absent. No API files were found to classify, but the expected top-level API area is not physically present. | -+| `serverside/` | FAIL | Canonical root is absent. No serverside files were found to classify, but the expected top-level serverside area is not physically present. | -+| `src/engine/` | FAIL | Most engine JS is under feature folders, but `src/engine/paletteList.js` is a root-level JS file and `src/engine/ui/*.css` contains CSS outside canonical asset/theme roots. | -+ -+## Non-Compliant JS Locations -+ -+### `toolbox/` -+ -+These active JavaScript files are outside the canonical tool asset structure: -+ -+- `toolbox/assets/assets-api-client.js` -+- `toolbox/assets/assets-upload-worker.js` -+- `toolbox/assets/assets.js` -+- `toolbox/colors/colors.js` -+- `toolbox/colors/palette-api-client.js` -+- `toolbox/controls/controls-api-client.js` -+- `toolbox/controls/controls.js` -+- `toolbox/game-configuration/game-configuration-api-client.js` -+- `toolbox/game-configuration/game-configuration.js` -+- `toolbox/game-design/game-design-api-client.js` -+- `toolbox/game-design/game-design.js` -+- `toolbox/game-hub/game-hub-api-client.js` -+- `toolbox/game-hub/game-hub.js` -+- `toolbox/game-journey/game-journey-api-client.js` -+- `toolbox/game-journey/game-journey.js` -+- `toolbox/idea-board/index.js` -+- `toolbox/messages/message-tts-service-registry.js` -+- `toolbox/messages/messages-api-client.js` -+- `toolbox/messages/messages.js` -+- `toolbox/objects/objects-api-client.js` -+- `toolbox/objects/objects.js` -+- `toolbox/tags/tags-api-client.js` -+- `toolbox/tags/tags.js` -+- `toolbox/text-to-speech/text2speech.js` -+- `toolbox/tool-registry-api-client.js` -+- `toolbox/toolRegistry.js` -+- `toolbox/tools-page-accordions.js` -+ -+Recommended target pattern: -+ -+- Tool-specific JS: `assets/toolbox/{tool-name}/js/index.js` -+- Shared toolbox JS: `assets/js/shared/` -+ -+### `src/engine/` -+ -+- `src/engine/paletteList.js` -+ -+Recommended target pattern: -+ -+- Move under a feature folder such as `src/engine/palette/` after import-impact review. -+ -+## Non-Compliant CSS Locations -+ -+### `toolbox/` -+ -+- None found. -+ -+### `assets/` -+ -+- None found. -+ -+### `src/engine/` -+ -+These CSS files are active style assets under engine source: -+ -+- `src/engine/ui/baseLayout.css` -+- `src/engine/ui/hubCommon.css` -+- `src/engine/ui/spriteEditor.css` -+ -+Recommended target pattern: -+ -+- Move shared UI/theme styling into `assets/theme-v2/css/` or create an approved engine UI style policy before relocation. -+ -+## Non-Compliant Test Locations -+ -+Canonical test roots from PR_035: ++## Findings + -+- `tests/toolbox/{tool-name}/` -+- `tests/engine/{feature-name}/` -+- `tests/api/{feature-name}/` -+- `tests/server/{feature-name}/` -+- `tests/js/shared/` -+- `tests/regression/` ++### P1 - PR #3 is stale against current Messages persistence + -+The audit found 494 files in non-canonical test locations: ++The primary service change targets `src/dev-runtime/messages/messages-sqlite-service.mjs`, but current `main` no longer has that file. Current `main` has `src/dev-runtime/messages/messages-postgres-service.mjs` and routes Messages through the PostgreSQL service in `src/dev-runtime/server/local-api-router.mjs`. + -+| Path | File Count | Examples | -+| --- | ---: | --- | -+| `tests/ai/` | 1 | `tests/ai/AIBehaviors.test.mjs` | -+| `tests/assets/` | 1 | `tests/assets/AssetLoaderSystem.test.mjs` | -+| `tests/audio/` | 1 | `tests/audio/AudioService.test.mjs` | -+| `tests/combat/` | 1 | `tests/combat/Combat.test.mjs` | -+| `tests/config/` | 1 | `tests/config/ConfigStore.test.mjs` | -+| `tests/core/` | 11 | `tests/core/EngineCoreBoundaryBaseline.test.mjs` | -+| `tests/dev-runtime/` | 31 | `tests/dev-runtime/AdminHealthOperations.test.mjs` | -+| `tests/entity/` | 1 | `tests/entity/Entity.test.mjs` | -+| `tests/events/` | 2 | `tests/events/EventBus.test.mjs` | -+| `tests/final/` | 11 | `tests/final/ReleaseReadinessSystems.test.mjs` | -+| `tests/fixtures/` | 52 | `tests/fixtures/assets/asset-scenarios.json` | -+| `tests/fx/` | 1 | `tests/fx/ParticleSystem.test.mjs` | -+| `tests/games/` | 35 | `tests/games/AsteroidsValidation.test.mjs` | -+| `tests/helpers/` | 11 | `tests/helpers/playwrightRepoServer.mjs` | -+| `tests/index.html` | 1 | `tests/index.html` | -+| `tests/input/` | 8 | `tests/input/InputService.test.mjs` | -+| `tests/persistence/` | 1 | `tests/persistence/StorageService.test.mjs` | -+| `tests/playwright/` | 44 | `tests/playwright/tools/GameJourneyTool.spec.mjs` | -+| `tests/playwright_installation.txt` | 1 | `tests/playwright_installation.txt` | -+| `tests/production/` | 3 | `tests/production/ProductionReadiness.test.mjs` | -+| `tests/README.md` | 1 | `tests/README.md` | -+| `tests/render/` | 1 | `tests/render/Renderer.test.mjs` | -+| `tests/replay/` | 2 | `tests/replay/ReplaySystem.test.mjs` | -+| `tests/results/` | 26 | `tests/results/playwright-results.json` | -+| `tests/run-tests.mjs` | 1 | `tests/run-tests.mjs` | -+| `tests/runtime/` | 81 | `tests/runtime/V2SessionPersistence.test.mjs` | -+| `tests/samples/` | 1 | `tests/samples/FullscreenRuleEnforcement.test.mjs` | -+| `tests/scenes/` | 3 | `tests/scenes/SceneManager.test.mjs` | -+| `tests/schemas/` | 1 | `tests/schemas/tool.manifest.schema.json` | -+| `tests/shared/` | 92 | `tests/shared/ProjectContract.test.mjs` | -+| `tests/testRunner.html` | 1 | `tests/testRunner.html` | -+| `tests/testRunner.js` | 1 | `tests/testRunner.js` | -+| `tests/tools/` | 57 | `tests/tools/ToolBoundaryEnforcement.test.mjs` | -+| `tests/validation/` | 3 | `tests/validation/samples.runtime.validation.report.json` | -+| `tests/vector/` | 1 | `tests/vector/VectorMath.test.mjs` | -+| `tests/world/` | 4 | `tests/world/WorldSystems.test.mjs` | ++Current Project Instructions also state that SQLite is deprecated, new persistence work targets PostgreSQL, and PRs introducing SQLite should be rejected. Merging PR #3 as-is would either fail to apply cleanly or reintroduce a deprecated service path. This is a blocking merge risk. + -+Generated Playwright result artifacts under `tests/results/` should be treated as cleanup/archive candidates rather than active test source. ++### P1 - The PR mixes the PR_006 implementation with future workstream BUILD specs + -+## Legacy Migration Candidates ++PR #3 adds PR_008, PR_010, PR_012, PR_014, and PR_016 BUILD files alongside the PR_006 implementation. GitHub already has a P1 review comment asking to split these future specs out of this PR. This violates the one-purpose PR rule and can pollute later review lanes with unmerged planning changes. + -+| Priority | Candidate | Reason | Recommended Handling | -+| --- | --- | --- | --- | -+| P0 | `tests/results/` | Generated result artifacts are tracked under active tests. | Move to ignored output or archive/report storage after owner approval. | -+| P1 | `toolbox/*/*.js` and shared `toolbox/*.js` | Active JS is colocated with HTML entries instead of canonical asset roots. | Migrate tool JS to `assets/toolbox/{tool-name}/js/index.js`; migrate shared JS to `assets/js/shared/`. | -+| P1 | `tests/dev-runtime/`, `tests/playwright/`, `tests/runtime/`, `tests/shared/`, `tests/tools/` | Large active test buckets conflict with PR_035 canonical test roots. | Split by ownership into `tests/toolbox/`, `tests/engine/`, `tests/api/`, `tests/server/`, `tests/js/shared/`, and `tests/regression/`. | -+| P2 | `src/engine/paletteList.js` | Root-level engine JS file is outside `src/engine/{feature-name}/`. | Move to an approved feature folder with import compatibility reviewed. | -+| P2 | `src/engine/ui/*.css` | CSS lives under engine source instead of theme/tool asset roots. | Move to `assets/theme-v2/css/` or define an explicit engine UI CSS policy. | -+| P3 | Missing `api/` and `serverside/` roots | Canonical target roots are absent. | Create roots when the first API/server migration needs them, with placeholder README only if governance permits. | ++### P2 - API payload expands reference details and should be reviewed against the current contract + -+## Prioritized Remediation List ++The patch adds `references`, `messageUsageCount`, `segmentUsageCount`, and `usageCount` to emotion profile payloads. That is a reasonable product direction, but it changes the API shape and exposes message and segment labels/previews in the profile response. In current main, similar behavior exists in the PostgreSQL service, so PR #3 should not be the vehicle for this API contract. + -+1. Remove or archive tracked generated artifacts under `tests/results/`. -+2. Create a staged migration plan for `toolbox/` JavaScript sidecars, starting with shared files (`toolRegistry.js`, `tool-registry-api-client.js`, `tools-page-accordions.js`). -+3. Standardize high-volume test buckets in phases: `tests/dev-runtime/`, `tests/playwright/`, `tests/runtime/`, `tests/shared/`, then `tests/tools/`. -+4. Move `src/engine/paletteList.js` into an engine feature folder after import-impact review. -+5. Resolve engine CSS placement for `src/engine/ui/*.css`. -+6. Add or defer canonical `api/` and `serverside/` roots with explicit owner-approved scope. ++### P2 - Targeted validation passed, but the required workspace lane failed + -+## Recommended Next Charlie PRs ++The PR report says targeted syntax, direct service probing, and a Messages Playwright path passed. It also says `npm run test:workspace-v2` failed. Even if those failures were outside the Messages scope at the time, the current merge decision should not ignore that validation gap. + -+- `PR_26172_CHARLIE_002-test-results-artifact-cleanup-plan` -+- `PR_26172_CHARLIE_003-toolbox-js-canonical-asset-migration-plan` -+- `PR_26172_CHARLIE_004-test-structure-standardization-plan` -+- `PR_26172_CHARLIE_005-engine-root-js-and-css-migration-plan` -+- `PR_26172_CHARLIE_006-api-serverside-root-readiness-plan` ++## Runtime Impact + -+## Requirement Checklist ++Runtime impact is high if merged as-is. The PR changes the Messages service layer, API payloads surfaced through the Messages tool, and UI behavior. It also touches a SQLite service file that is absent on current `main`, while the current app uses PostgreSQL-backed Messages code. + -+| Requirement | Status | Evidence | -+| --- | --- | --- | -+| Follow ProjectInstructions | PASS | Read active `docs_build/dev/ProjectInstructions/` governance before work. | -+| Start from latest main | PASS | Main pulled and branch created from `c4a495f0aa8e32d499ca64555c4a3547e6fcb298`. | -+| Worktree clean before work | PASS | `git status --short` returned no output before edits. | -+| Audit PR_034 structure | PASS | Compared scoped paths to canonical toolbox, assets, engine, API, and serverside roots. | -+| Audit PR_035 tests | PASS | Compared `tests/` contents to canonical test roots. | -+| Audit PR_036 legacy migration policy | PASS | Classified migration candidates without moving or deleting files. | -+| Review `toolbox/` | PASS | Identified 27 non-compliant JS files. | -+| Review `assets/` | PASS | Found no non-compliant active JS/CSS in `assets/`. | -+| Review `tests/` | PASS | Identified 494 files in non-canonical test locations. | -+| Review `api/` | PASS | Directory absent; recorded as structural failure. | -+| Review `serverside/` | PASS | Directory absent; recorded as structural failure. | -+| Review `src/engine/` | PASS | Identified root JS and engine CSS placement issues. | -+| No executable implementation changes | PASS | Audit/report-only scope. | -+| Create audit report | PASS | This file. | -+| Create standard Codex reports | PASS | `docs_build/dev/reports/codex_review.diff` and `docs_build/dev/reports/codex_changed_files.txt` exist. | -+| Create ZIP artifact | PASS | `tmp/PR_26172_CHARLIE_001-repository-compliance-audit_delta.zip` exists. | ++## DB/API Impact + -+## Validation Lane Report ++- Adds two usage queries per emotion profile in the SQLite service. ++- Adds aggregate usage fields to emotion profile API payloads. ++- Adds `references` details containing message and segment reference information. ++- Blocks active-to-inactive changes when a profile has references. ++- Does not add a delete endpoint. ++- Conflicts with the current PostgreSQL-only persistence direction. + -+- `git diff --check`: PASS. -+- Report exists: PASS. -+- Required Codex reports exist: PASS. -+- ZIP artifact exists: PASS. -+- Runtime source files changed: PASS, changed files are limited to `docs_build/dev/reports/`. -+- Playwright: SKIP, audit-only documentation/report PR. -+- Samples: SKIP, audit-only documentation/report PR. ++## UI Impact + -+## Manual Validation Notes ++- Adds a Usage column to the Emotion Profiles table. ++- Updates the loading row colspan from 3 to 4. ++- Renders usage count in `toolbox/messages/messages.js`. ++- Refreshes emotion profile state and select options after segment reload. ++- Changes volume, pitch, and rate input step values from `0.05` to `0.01`. + -+- The audit intentionally did not move files, delete generated artifacts, create canonical roots, or modify executable source. -+- `api/` and `serverside/` were missing from the working tree; this was recorded as a structural finding, not remediated in this PR. -+- Non-canonical test locations were counted by top-level path under `tests/` to keep the report actionable without rewriting the test tree. -+- Tracked generated artifacts under `tests/results/` are listed as a high-priority cleanup candidate because they are active repository files but not active test source. -diff --git a/docs_build/dev/reports/PR_26172_CHARLIE_002-test-results-artifact-cleanup.md b/docs_build/dev/reports/PR_26172_CHARLIE_002-test-results-artifact-cleanup.md -new file mode 100644 -index 000000000..17898495f ---- /dev/null -+++ b/docs_build/dev/reports/PR_26172_CHARLIE_002-test-results-artifact-cleanup.md -@@ -0,0 +1,129 @@ -+# PR_26172_CHARLIE_002 Test Results Artifact Cleanup ++UI risk is moderate. The usage column is useful, but table layout and edit/select state should be retested against the current Theme V2 Messages UI. + -+## Scope ++## Theme V2 Compliance + -+Clean up generated test result artifacts under `tests/results/` after the Charlie repository compliance audit identified that path as a high-priority cleanup candidate. ++PASS with review note. The code patch does not add inline styles, inline scripts, page-local CSS, or tool-local CSS. The UI changes are table markup and existing DOM rendering changes. A visual check is still recommended because the added Usage column changes table density. + -+Source audit: ++## Auth/Session Impact + -+- `docs_build/dev/reports/PR_26172_CHARLIE_001-repository-compliance-audit.md` ++No direct auth or session files are touched. The PR uses existing Local API paths. Auth/session risk is low, but any future rework should validate the Messages tool under the current session model because current workspace validation previously reported session-related failures outside the Messages scope. + -+This PR does not modify runtime source and does not move unrelated tests. ++## Browser-Owned Data Risk + -+## Team Ownership ++Low direct risk. The patch does not add `localStorage`, browser-owned persistence, or persisted browser-only message state. Data remains server-owned through the Local API/service boundary. + -+- TEAM token: CHARLIE -+- Ownership classification: governance / repository hygiene / diagnostics -+- TEAM ownership result: PASS ++## Deletion/In-Use Guard Risk + -+## Branch Validation ++The active-to-inactive guard is directionally correct: referenced Emotion Profiles should not be deactivated while messages or segments still reference them. Remaining risks: + -+| Requirement | Status | Evidence | -+| --- | --- | --- | -+| Started from latest main | PASS | `main` was pulled before branch creation; source commit `f2b50ac9d79256df3a7716ac4eff21f3a4303bb3`. | -+| Worktree clean before branch | PASS | `git status --short` returned no output before branch creation. | -+| Local/origin sync before branch | PASS | `git rev-list --left-right --count HEAD...origin/main` returned `0 0`. | -+| PR branch created from main | PASS | Branch `pr/26172-CHARLIE-002-test-results-artifact-cleanup` was created from latest `main`. | ++- The guard lives in the stale SQLite service in PR #3. ++- There is no database-level constraint shown in the PR. ++- The PR does not address delete behavior beyond saying no delete endpoint was added. ++- The current PostgreSQL service already contains a comparable in-use guard, so the PR is superseded for this behavior. + -+## Files Reviewed ++## Test Coverage + -+`git ls-files tests/results` returned no tracked files. ++Covered in PR #3: + -+The local ignored `tests/results/` folder contained generated Playwright/report output: ++- Usage counts for one message reference and one segment reference. ++- API rejection when deactivating a referenced `Urgent` profile. ++- UI diagnostic when attempting to deactivate that referenced profile. ++- Active status remains after rejected deactivation. + -+- `tests/results/artifacts/.last-run.json` -+- `tests/results/artifacts/tools-MidiStudioV2-MIDI-St-3c5a9-multi-song-manifest-payload-playwright/trace.zip` -+- `tests/results/artifacts/tools-MidiStudioV2-MIDI-St-752e4-on-and-timeline-scroll-sync-playwright/trace.zip` -+- `tests/results/artifacts/tools-MidiStudioV2-MIDI-St-c50c5-m-Tool-Mode-standalone-save-playwright/trace.zip` -+- `tests/results/playwright-results.json` -+- `tests/results/report/data/09daf0cfe8750af5e9e5bb22161367f97296f4fd.zip` -+- `tests/results/report/data/a9ba8bc1c6a629055b981a6f385fa4de3e42a79d.zip` -+- `tests/results/report/data/b1dc1da730cbd5e9adc334a6f385fa4de3e42a79d.zip` -+- `tests/results/report/data/c150573559f5367f4ec5724abb7a55798abcdff9.zip` -+- `tests/results/report/index.html` -+- `tests/results/report/trace/assets/codeMirrorModule-DS0FLvoc.js` -+- `tests/results/report/trace/assets/defaultSettingsView-GTWI-W_B.js` -+- `tests/results/report/trace/codeMirrorModule.DYBRYzYX.css` -+- `tests/results/report/trace/codicon.DCmgc-ay.ttf` -+- `tests/results/report/trace/defaultSettingsView.B4dS75f0.css` -+- `tests/results/report/trace/index.C5466mMT.js` -+- `tests/results/report/trace/index.CzXZzn5A.css` -+- `tests/results/report/trace/index.html` -+- `tests/results/report/trace/manifest.webmanifest` -+- `tests/results/report/trace/playwright-logo.svg` -+- `tests/results/report/trace/snapshot.html` -+- `tests/results/report/trace/sw.bundle.js` -+- `tests/results/report/trace/uiMode.Btcz36p_.css` -+- `tests/results/report/trace/uiMode.Vipi55dB.js` -+- `tests/results/report/trace/uiMode.html` -+- `tests/results/report/trace/xtermModule.DYP7pi_n.css` ++Not sufficiently covered for merge: + -+## Files Removed Or Retained ++- Current PostgreSQL service path on `main`. ++- Empty and multi-profile usage aggregation. ++- Unreferenced profile deactivation remains allowed. ++- Table layout or visual regression after adding the Usage column. ++- Full workspace lane is not green in the PR evidence. + -+| Category | Status | Notes | -+| --- | --- | --- | -+| Tracked files under `tests/results/` | None removed | No tracked files existed under `tests/results/`. | -+| Local ignored generated artifacts under `tests/results/` | Removed from workspace | Removed only after verifying the resolved target path was inside the repository. | -+| Active test source | Retained | No active test source was found under `tests/results/`. | -+| Fixture or baseline data | Retained | No committed fixture or baseline dependency was found under `tests/results/`. | ++## Playwright Recommendation + -+## Reference And Dependency Check ++Do not rely on the old PR #3 Playwright result for a current merge. If the behavior is still desired, retest on a fresh PostgreSQL-based branch from current `main` with: + -+| Check | Status | Evidence | -+| --- | --- | --- | -+| Active tracked files under `tests/results/` | PASS | `git ls-files tests/results` returned no output. | -+| Tracked ignored files under `tests/results/` | PASS | `git ls-files -c -i --exclude-standard tests/results` returned no output. | -+| Ignored local generated files under `tests/results/` | PASS | `git ls-files -o -i --exclude-standard tests/results` listed only Playwright/report artifacts. | -+| Active config uses `tmp/test-results/` | PASS | `playwright.config.cjs` writes output, artifacts, HTML report, and JSON report under `tmp/test-results/`. | -+| Active references to `tests/results/` | PASS | Active config/test/docs search returned no required source or fixture dependency. | -+| Historical references retained | PASS | References in `archive/` and historical `docs_build/dev/reports/` were not modified. | ++- Targeted Messages tool Playwright coverage for usage count display. ++- API/service test coverage for referenced and unreferenced Emotion Profile active-state changes. ++- A visual/table-density check for the Emotion Profiles table. ++- The current workspace validation lane required by Project Instructions. + -+## Ignore Rule Changes ++## Merge/Hold/Close Recommendation + -+Updated `.gitignore` to make generated test-output protection explicit: ++Recommendation: close PR #3 as superseded after OWNER approval. + -+- Kept `tests/results/`. -+- Added `tests/results/**`. -+- Kept `tmp/test-results/`. -+- Added `tmp/test-results/**`. -+- Confirmed `tmp/` remains ignored. ++Reason: + -+Ignore probe: ++- It is not mergeable on GitHub. ++- It targets a removed SQLite service path. ++- Current governance says PostgreSQL is the active persistence direction. ++- Current `main` already has Messages PostgreSQL usage-count and in-use guard behavior. ++- It mixes future BUILD specs into a PR_006 implementation branch. + -+- `git check-ignore -v tests/results/probe.txt` resolves to `.gitignore`. -+- `git check-ignore -v tmp/test-results/probe.txt` resolves to `.gitignore`. ++Do not merge PR #3. If owner wants any missing behavior from PR #3 retained, create a fresh Bravo branch from current `main` and implement only the missing PostgreSQL-backed Messages/Emotion Profiles behavior. + +## Requirement Checklist + -+| Requirement | Status | Evidence | ++| Requirement | Result | Notes | +| --- | --- | --- | -+| Confirm Project Instructions were reviewed | PASS | Read `docs_build/dev/ProjectInstructions/README.txt`, `PROJECT_INSTRUCTIONS.md`, branch/workflow governance, team ownership, and artifact/reporting rules. | -+| Use PR_26172_CHARLIE_001 findings | PASS | This cleanup is based on the P0 `tests/results/` finding. | -+| Review `tests/results/` | PASS | Reviewed tracked, ignored, and local generated contents. | -+| Confirm generated artifacts, not active source | PASS | Files were Playwright JSON, HTML report, trace assets, and zipped trace/report data. | -+| Search references to `tests/results/` files | PASS | No active source/fixture dependency found; historical references retained. | -+| Remove tracked generated artifacts if safe | PASS | No tracked generated artifacts existed to remove. | -+| Add/update ignore rules | PASS | `.gitignore` now explicitly includes `tests/results/**` and `tmp/test-results/**`. | -+| Do not remove active test source | PASS | No active test source removed. | -+| Do not modify runtime source | PASS | No runtime source changed. | -+| Do not move unrelated tests | PASS | No test files were moved. | -+| Stop gate not triggered | PASS | No `tests/results/` file was required as active source, fixture data, or committed baseline data. | -+| Create required reports | PASS | `docs_build/dev/reports/codex_review.diff` and `docs_build/dev/reports/codex_changed_files.txt` exist. | -+| Create ZIP artifact | PASS | `tmp/PR_26172_CHARLIE_002-test-results-artifact-cleanup_delta.zip` exists. | ++| Start from main | PASS | Started from clean `main` at `5415f6675d7a0f10931b83368948a83df98d8021`. | ++| Hard stop if not main, dirty, or unsynced | PASS | Gate passed: branch `main`, clean worktree, local/origin `0 0`. | ++| Read all Project Instructions | PASS | Read `docs_build/dev/ProjectInstructions/` before report generation. | ++| Review GitHub PR #3 only | PASS | Report covers only PR #3. | ++| Pull PR #3 diff | PASS | Used GitHub PR metadata and file diff data for PR #3. | ++| Include changed code files only | PASS | Included dedicated changed-code-files table. | ++| Include full changed file list | PASS | Included all 15 changed files with status and line counts. | ++| Include runtime impact | PASS | Included. | ++| Include DB/API impact | PASS | Included. | ++| Include UI impact | PASS | Included. | ++| Include Theme V2 compliance | PASS | Included. | ++| Include auth/session impact | PASS | Included. | ++| Include browser-owned data risk | PASS | Included. | ++| Include deletion/in-use guard risk | PASS | Included. | ++| Include test coverage | PASS | Included. | ++| Include Playwright recommendation | PASS | Included. | ++| Include merge/hold/close recommendation | PASS | Included: close as superseded after OWNER approval. | ++| Do not merge PR #3 | PASS | No merge performed. | ++| Do not close PR #3 | PASS | No close performed. | ++| Do not delete branches | PASS | No branch deletion performed. | ++| Do not modify runtime code | PASS | Only report artifacts changed. | ++| Create repo-structured ZIP under `tmp/` | PASS | Required path: `tmp/PR_26175_BRAVO_001-pr-003-messages-emotion-profiles-code-review_delta.zip`. | + +## Validation Lane Report + -+- `git diff --check`: PASS. -+- Cleanup limited to generated artifacts under `tests/results/`: PASS. -+- Ignore rule prevents recommit: PASS. -+- Runtime source files changed: PASS, no runtime source files changed. -+- Required reports exist: PASS. -+- ZIP artifact exists: PASS. -+- Playwright: SKIP, ignore/report-only cleanup with no active test or runtime source changes. -+- Samples: SKIP, no sample files changed. -+ -+## Manual Validation Notes -+ -+- The local ignored `tests/results/` directory was deleted from the workspace only after path verification showed it was inside the repository root. -+- Repository history already contains `docs_build/dev/reports/docs_archive_test_output_cleanup_report.md`, which documents the prior migration of generated test output from `tests/results/` to `tmp/test-results/`. -+- This PR preserves historical report/archive references and only hardens the active ignore rule. -diff --git a/docs_build/dev/reports/PR_26172_CHARLIE_003-src-dev-runtime-test-migration-audit.md b/docs_build/dev/reports/PR_26172_CHARLIE_003-src-dev-runtime-test-migration-audit.md -new file mode 100644 -index 000000000..3a9529821 ---- /dev/null -+++ b/docs_build/dev/reports/PR_26172_CHARLIE_003-src-dev-runtime-test-migration-audit.md -@@ -0,0 +1,94 @@ -+# PR_26172_CHARLIE_003 Src Dev-Runtime Test Migration Audit -+ -+## Scope -+ -+Audit `src/dev-runtime/` for test files that should move into canonical test paths. -+ -+Canonical test targets reviewed: -+ -+- `tests/server/` -+- `tests/api/` -+- `tests/engine/` -+- `tests/toolbox/` -+ -+This PR is audit-only. No files were moved and no runtime source was modified. -+ -+## Branch Validation -+ -+| Requirement | Status | Evidence | -+| --- | --- | --- | -+| Started from latest main | PASS | Workstream branch was created from `main` at `b97893c78dcfed05d5b0de0c7d03127ec5575292`. | -+| Worktree clean before branch | PASS | `git status --short` returned no output before branch creation. | -+| Local/origin sync before branch | PASS | `git rev-list --left-right --count HEAD...origin/main` returned `0 0`. | -+| Active branch | PASS | `PR_26172_CHARLIE_repository-compliance-stack`. | -+| Team ownership | PASS | Team Charlie owns governance, infrastructure, operations, diagnostics, and system health. | -+ -+## Files Reviewed -+ -+`src/dev-runtime/` currently contains runtime, server, persistence, admin, storage, seed, auth, and dev utility modules. -+ -+Test-pattern scans found: -+ -+- `rg --files src/dev-runtime -g "*.test.*" -g "*.spec.*" -g "*Test*" -g "*Spec*"`: no matches. -+- `rg -n "node:test|describe\\(|it\\(|test\\(|assert\\." src/dev-runtime -g "*.js" -g "*.mjs" -g "*.cjs"`: no test harness matches. -+ -+The `src/dev-runtime/testing/` directory was reviewed specifically: -+ -+- `src/dev-runtime/testing/supabase-dev-auth-test-user-cleanup.mjs` -+- `src/dev-runtime/testing/supabase-dev-creator-identity-seed-sync.mjs` -+ -+These are DEV-only utility/runtime support modules, not test files. They are imported by scripts and covered by tests under `tests/dev-runtime/`. -+ -+## Matching Canonical Target Paths -+ -+No direct file moves are recommended because no test files live under `src/dev-runtime/`. -+ -+Existing coverage already lives in active test locations: -+ -+| Runtime utility | Existing coverage | Canonical target recommendation | -+| --- | --- | --- | -+| `src/dev-runtime/testing/supabase-dev-auth-test-user-cleanup.mjs` | `tests/dev-runtime/SupabaseDevAuthTestUserCleanup.test.mjs` | Future test-structure migration should move coverage to `tests/server/dev-runtime/SupabaseDevAuthTestUserCleanup.test.mjs` or an owner-approved equivalent under `tests/server/`. | -+| `src/dev-runtime/testing/supabase-dev-creator-identity-seed-sync.mjs` | `tests/dev-runtime/SupabaseDevCreatorIdentitySeedSync.test.mjs` | Future test-structure migration should move coverage to `tests/server/dev-runtime/SupabaseDevCreatorIdentitySeedSync.test.mjs` or an owner-approved equivalent under `tests/server/`. | -+ -+## Next Migration PRs -+ -+Exact next PR recommendations: -+ -+1. `PR_26172_CHARLIE_004-src-dev-runtime-low-risk-test-move` -+ - Expected result: STOP GATE unless new low-risk `src/dev-runtime` test files appear before execution. -+ - Reason: this audit found no movable tests under `src/dev-runtime/`. -+2. `PR_26172_CHARLIE_010-dev-runtime-test-structure-plan` -+ - Scope: define the owner-approved canonical destination for existing `tests/dev-runtime/` coverage. -+ - Reason: the broader test-standardization issue is under `tests/dev-runtime/`, not under `src/dev-runtime/`. -+3. `PR_26172_CHARLIE_011-dev-runtime-server-test-folder-migration` -+ - Scope: move clear server/dev-runtime tests from `tests/dev-runtime/` into `tests/server/dev-runtime/` after the canonical destination is approved. -+ -+## Requirement Checklist -+ -+| Requirement | Status | Evidence | ++| Validation | Result | Evidence | +| --- | --- | --- | -+| Review and use ProjectInstructions | PASS | Read active ProjectInstructions, branch governance, team ownership, and artifact/reporting rules. | -+| Review `src/dev-runtime/` | PASS | Scoped file and content scans completed. | -+| Identify test files under `src/dev-runtime/` | PASS | No test files found. | -+| Identify matching canonical target paths | PASS | No direct move targets; future coverage targets documented for existing `tests/dev-runtime/` files. | -+| Do not move files | PASS | No files moved. | -+| Do not modify runtime source | PASS | Runtime source unchanged. | -+| Confirm no executable runtime source changed | PASS | Changed files are reports only for this PR. | -+| Audit lists exact next migration PRs | PASS | See "Next Migration PRs". | -+| ZIP exists | PASS | `tmp/PR_26172_CHARLIE_003-src-dev-runtime-test-migration-audit_delta.zip` exists. | -+ -+## Validation Lane Report -+ -+- No executable runtime source changed: PASS. -+- Audit lists exact next migration PRs: PASS. -+- `git diff --check`: PASS. -+- Required reports exist: PASS. -+- ZIP artifact exists: PASS. -+- Playwright: SKIP, audit-only scope. -+- Samples: SKIP, audit-only scope. ++| Branch gate | PASS | Branch created after clean/synced `main` gate passed. | ++| GitHub PR metadata | PASS | Fetched PR #3 metadata from GitHub. | ++| GitHub PR file list | PASS | Fetched all changed files for PR #3 from GitHub. | ++| Current-main persistence comparison | PASS | Confirmed current `main` has `messages-postgres-service.mjs` and does not have `messages-sqlite-service.mjs`. | ++| Report-only scope check | PASS | Only required report files changed. | ++| Runtime tests | NOT RUN | Report-only PR; no runtime code changed. | ++| Playwright tests | NOT RUN | Report-only PR; no UI/runtime code changed. | ++| Samples | NOT RUN | Report-only PR; no samples changed. | + +## Manual Validation Notes + -+- The word `testing` in `src/dev-runtime/testing/` is directory naming for DEV-only utility modules, not evidence of colocated test source. -+- Existing active tests for those utilities are already outside `src/dev-runtime/`, under `tests/dev-runtime/`. -+- PR_004 should stop-gate if no new low-risk source-path test files are present when it runs. -diff --git a/docs_build/dev/reports/PR_26172_CHARLIE_004-src-dev-runtime-low-risk-test-move.md b/docs_build/dev/reports/PR_26172_CHARLIE_004-src-dev-runtime-low-risk-test-move.md -new file mode 100644 -index 000000000..ec6a12d10 ---- /dev/null -+++ b/docs_build/dev/reports/PR_26172_CHARLIE_004-src-dev-runtime-low-risk-test-move.md -@@ -0,0 +1,58 @@ -+# PR_26172_CHARLIE_004 Src Dev-Runtime Low-Risk Test Move -+ -+## Scope -+ -+Move only low-risk `src/dev-runtime` test files into canonical `tests/` paths. -+ -+## Stop Gate Result -+ -+Status: STOP GATE - no low-risk moves exist. -+ -+Reason: -+ -+- PR_26172_CHARLIE_003 found no test files under `src/dev-runtime/`. -+- PR_004 re-ran the low-risk eligibility check and found no `*.test.*`, `*.spec.*`, `*Test*`, or `*Spec*` files under `src/dev-runtime/`. -+- The two files under `src/dev-runtime/testing/` are DEV-only utility modules imported by scripts, not test source. -+ -+Because there are no source-path test files, moving files would either move runtime/dev utility code or expand scope into broader `tests/dev-runtime/` standardization. Both are outside this PR. -+ -+## Files Moved -+ -+None. -+ -+## Retained Files -+ -+| Path | Reason Retained | -+| --- | --- | -+| `src/dev-runtime/testing/supabase-dev-auth-test-user-cleanup.mjs` | DEV-only cleanup utility; not a test file. | -+| `src/dev-runtime/testing/supabase-dev-creator-identity-seed-sync.mjs` | DEV-only seed sync utility; not a test file. | -+ -+## Validation -+ -+| Check | Status | Evidence | -+| --- | --- | --- | -+| PR_003 audit used | PASS | PR_003 report found no movable source-path tests. | -+| Low-risk move scan rerun | PASS | `rg --files src/dev-runtime -g "*.test.*" -g "*.spec.*" -g "*Test*" -g "*Spec*"` returned no matches. | -+| Runtime source unchanged | PASS | No runtime source files changed. | -+| Old paths no longer referenced | N/A | No files were moved, so no path reference update was required. | -+| New paths follow canonical tests roots | N/A | No files were moved. | -+| Targeted tests for moved files | SKIP | No files were moved. | -+| ZIP exists | PASS | `tmp/PR_26172_CHARLIE_004-src-dev-runtime-low-risk-test-move_delta.zip` exists. | -+ -+## Requirement Checklist -+ -+| Requirement | Status | Evidence | -+| --- | --- | --- | -+| Use PR_003 audit | PASS | PR_003 was the input for this stop gate. | -+| Move only obvious low-risk files | PASS | No candidates existed, so no move was attempted. | -+| Update package/test references if required | N/A | No move occurred. | -+| Do not change test logic unless imports/paths require it | PASS | No test logic changed. | -+| Do not modify runtime source | PASS | Runtime source unchanged. | -+| Do not move ambiguous files | PASS | No ambiguous files moved. | -+| Continue to next audit PR if safe | PASS | Next scope is audit-only and independent. | -+ -+## Manual Validation Notes -+ -+- This is a successful stop-gate deliverable, not a failed PR. -+- The broader migration target is existing `tests/dev-runtime/` coverage, which should be planned separately under an owner-approved canonical destination. -+- No executable implementation files were changed. -diff --git a/docs_build/dev/reports/PR_26172_CHARLIE_005-scattered-tool-js-css-audit.md b/docs_build/dev/reports/PR_26172_CHARLIE_005-scattered-tool-js-css-audit.md -new file mode 100644 -index 000000000..e4f37ddca ---- /dev/null -+++ b/docs_build/dev/reports/PR_26172_CHARLIE_005-scattered-tool-js-css-audit.md -@@ -0,0 +1,107 @@ -+# PR_26172_CHARLIE_005 Scattered Tool JS/CSS Audit -+ -+## Scope -+ -+Audit `toolbox/` and `assets/` for non-canonical tool JavaScript and CSS locations against the canonical repository structure. -+ -+Canonical tool asset targets: -+ -+- `assets/toolbox/{tool}/js/index.js` -+- `assets/toolbox/{tool}/css/index.css` -+ -+This PR is audit-only. No implementation files were moved. -+ -+## Summary -+ -+| Finding | Count | Status | -+| --- | ---: | --- | -+| Toolbox HTML pages reviewed | 50 | PASS | -+| Tool JS files under `toolbox/` | 27 | FAIL | -+| Tool CSS files under `toolbox/` | 0 | PASS | -+| Canonical tool JS files under `assets/toolbox/` | 0 | FAIL | -+| Canonical tool CSS files under `assets/toolbox/` | 0 | N/A | -+| Non-canonical tool JS/CSS under `assets/toolbox/` | 0 | PASS | -+| Inline `
Database Health - Postgres Only