From e4541d63719ab777b0654c8fecf4b13237d31256 Mon Sep 17 00:00:00 2001 From: DavidQ Date: Sat, 20 Jun 2026 14:34:43 -0400 Subject: [PATCH] PR_26171_061 text to speech engine audio feature parity --- ...71_061-engine-audio-ownership-checklist.md | 25 + ...71_061-instruction-compliance-checklist.md | 32 + .../PR_26171_061-manual-validation-notes.md | 27 + ...71_061-old-tts-feature-parity-checklist.md | 48 + ...text2speech-engine-audio-feature-parity.md | 71 + .../dev/reports/PR_26171_061-validation.md | 40 + .../dev/reports/codex_changed_files.txt | 84 +- docs_build/dev/reports/codex_review.diff | 2057 +++++++++++++++-- .../reports/coverage_changed_js_guardrail.txt | 8 +- .../reports/playwright_v8_coverage_report.txt | 37 +- src/engine/audio/TextToSpeechEngine.js | 208 +- .../tools/TextToSpeechFunctional.spec.mjs | 30 + toolbox/text-to-speech/index.html | 119 +- toolbox/text-to-speech/text2speech.js | 853 +++++-- 14 files changed, 3149 insertions(+), 490 deletions(-) create mode 100644 docs_build/dev/reports/PR_26171_061-engine-audio-ownership-checklist.md create mode 100644 docs_build/dev/reports/PR_26171_061-instruction-compliance-checklist.md create mode 100644 docs_build/dev/reports/PR_26171_061-manual-validation-notes.md create mode 100644 docs_build/dev/reports/PR_26171_061-old-tts-feature-parity-checklist.md create mode 100644 docs_build/dev/reports/PR_26171_061-text2speech-engine-audio-feature-parity.md create mode 100644 docs_build/dev/reports/PR_26171_061-validation.md diff --git a/docs_build/dev/reports/PR_26171_061-engine-audio-ownership-checklist.md b/docs_build/dev/reports/PR_26171_061-engine-audio-ownership-checklist.md new file mode 100644 index 000000000..5e18e0955 --- /dev/null +++ b/docs_build/dev/reports/PR_26171_061-engine-audio-ownership-checklist.md @@ -0,0 +1,25 @@ +# PR_26171_061 Engine Audio Ownership Checklist + +## Engine Ownership + +- PASS: Reusable TTS voice filtering lives in `src/engine/audio/TextToSpeechEngine.js`. +- PASS: Reusable TTS preset shaping lives in `src/engine/audio/TextToSpeechEngine.js`. +- PASS: Reusable TTS queue item helpers live in `src/engine/audio/TextToSpeechEngine.js`. +- PASS: Existing defaults remain in `src/engine/audio/TextToSpeechDefaults.js`. +- PASS: Active toolbox UI consumes the engine module instead of duplicating all engine behavior locally. + +## Active Tool Ownership + +- PASS: Active tool remains under `toolbox/text-to-speech/`. +- PASS: Active HTML remains Theme V2 only. +- PASS: Active JavaScript remains external. +- PASS: Active tool owns DOM wiring, status display, and control event handling. +- PASS: Active tool does not create database behavior. + +## Boundary Checks + +- PASS: No `tools/text2speech/` path was created. +- PASS: Archived `old_text2speech-V2` was used as a read-only functionality sample. +- PASS: Archived tool files were not activated in navigation. +- PASS: Browser SpeechSynthesis is the implemented local provider. +- PASS: Planned providers remain documented but do not block browser preview. diff --git a/docs_build/dev/reports/PR_26171_061-instruction-compliance-checklist.md b/docs_build/dev/reports/PR_26171_061-instruction-compliance-checklist.md new file mode 100644 index 000000000..ffb2cb288 --- /dev/null +++ b/docs_build/dev/reports/PR_26171_061-instruction-compliance-checklist.md @@ -0,0 +1,32 @@ +# PR_26171_061 Instruction Compliance Checklist + +## Pre-Work Gate + +- PASS: Read `docs_build/dev/PROJECT_INSTRUCTIONS.md` before file changes. +- PASS: Read `docs_build/dev/PROJECT_MULTI_PC.txt` before file changes. +- PASS: Reported instruction compliance PASS/FAIL before file changes. +- PASS: `git branch --show-current` reported `main` before branch creation. +- PASS: `git checkout main` completed before branch creation. +- PASS: `git pull origin main` completed before branch creation. +- PASS: `git status` reported a clean working tree before branch creation. +- PASS: Created branch `pr/26171-061-text2speech-engine-audio-feature-parity` from clean latest `main`. + +## Owner And Parity + +- PASS: PR number `061` is odd. +- PASS: Odd parity maps to Laptop / Environment 2. +- PASS: Text To Speech / TTS is owned by Laptop / Environment 2. +- PASS: Implementation path matches active owner path: `toolbox/text-to-speech/`. +- PASS: Shared engine path matches approved reusable audio path: `src/engine/audio/`. + +## Hard Stop Checks + +- PASS: Instructions were present and readable. +- PASS: Branch workflow could be followed. +- PASS: Repository was clean before branching. +- PASS: No owner mismatch. +- PASS: No parity mismatch. +- PASS: No wrong implementation path. +- PASS: Scoped validation was not skipped. +- PASS: Required reports were created. +- PASS: Required repo ZIP was created. diff --git a/docs_build/dev/reports/PR_26171_061-manual-validation-notes.md b/docs_build/dev/reports/PR_26171_061-manual-validation-notes.md new file mode 100644 index 000000000..3341b14ba --- /dev/null +++ b/docs_build/dev/reports/PR_26171_061-manual-validation-notes.md @@ -0,0 +1,27 @@ +# PR_26171_061 Manual Validation Notes + +## Manual Review + +- Confirmed active Text To Speech path is `toolbox/text-to-speech/`. +- Confirmed no `tools/text2speech/` directory was created. +- Confirmed old functionality sample path is `archive/v1-v2/tools/old_text2speech-V2/`. +- Confirmed active HTML has external scripts only. +- Confirmed active HTML has no inline style attributes or inline event handlers. +- Confirmed no database files were changed. + +## Automated Browser Coverage Used For Manual Equivalents + +- Opened active Text To Speech page through targeted Playwright validation. +- Verified browser voices render. +- Verified restored control groups render. +- Verified preset shaping updates pitch and volume values. +- Verified named sentence Add, Duplicate, and Delete. +- Verified output summary includes queue JSON. +- Verified Speak, Pause, Resume, and Stop call the browser SpeechSynthesis mock. +- Verified unavailable SpeechSynthesis shows actionable error. + +## Out Of Scope Manual Checks + +- No paid provider was manually exercised. +- No generated audio file export was manually exercised. +- No database behavior was manually exercised. diff --git a/docs_build/dev/reports/PR_26171_061-old-tts-feature-parity-checklist.md b/docs_build/dev/reports/PR_26171_061-old-tts-feature-parity-checklist.md new file mode 100644 index 000000000..e09e0f974 --- /dev/null +++ b/docs_build/dev/reports/PR_26171_061-old-tts-feature-parity-checklist.md @@ -0,0 +1,48 @@ +# PR_26171_061 Old TTS Feature Parity Checklist + +Functionality sample: `archive/v1-v2/tools/old_text2speech-V2/` + +## Controls And Options + +- PASS: Gender helper filter restored. +- PASS: Language filter restored. +- PASS: Voice dropdown restored. +- PASS: Voice details restored. +- PASS: Voice Age select restored. +- PASS: Character Preset select restored. +- PASS: SSML-like Preset select restored. +- PASS: Volume slider restored with visible value. +- PASS: Rate / Speed slider restored with visible value. +- PASS: Pitch slider restored with visible value. +- PASS: Name field restored. +- PASS: Text To Speak editor restored. + +## Queue And JSON + +- PASS: Named Sentences queue restored. +- PASS: Add named sentence restored. +- PASS: Duplicate named sentence restored. +- PASS: Delete named sentence restored. +- PASS: Output Summary JSON restored. +- PASS: Import JSON restored for standalone launch. +- PASS: Copy JSON restored for standalone launch. +- PASS: Export JSON restored for standalone launch. +- PASS: URL JSON source loading restored through `samplePresetPath`. + +## Playback And Status + +- PASS: Speak restored. +- PASS: Pause restored when browser support exists. +- PASS: Resume restored when browser support exists. +- PASS: Stop restored. +- PASS: Clearable status log restored. +- PASS: Actionable unavailable-browser error restored. +- PASS: No placeholder provider-blocking behavior remains for browser preview. + +## Workspace + +- PASS: Project Workspace launch detection restored. +- PASS: Standalone JSON actions hide during Project Workspace launch. +- PASS: Return to Project Workspace action restored. +- PASS: Project Workspace toolState loading restored. +- PASS: Project Workspace dirty-state writeback restored. diff --git a/docs_build/dev/reports/PR_26171_061-text2speech-engine-audio-feature-parity.md b/docs_build/dev/reports/PR_26171_061-text2speech-engine-audio-feature-parity.md new file mode 100644 index 000000000..b2f4a44d3 --- /dev/null +++ b/docs_build/dev/reports/PR_26171_061-text2speech-engine-audio-feature-parity.md @@ -0,0 +1,71 @@ +# PR_26171_061 Text To Speech Engine Audio Feature Parity + +## Summary + +Restored active Text To Speech feature parity from the archived `old_text2speech-V2` functionality sample while keeping active implementation in `toolbox/text-to-speech/` and reusable engine behavior in `src/engine/audio/`. + +## Changed Scope + +- Rebuilt reusable Text To Speech voice filtering, preset shaping, and queue-item helpers in `src/engine/audio/TextToSpeechEngine.js`. +- Expanded `toolbox/text-to-speech/index.html` with Theme V2 controls for the old V2 feature set. +- Rebuilt `toolbox/text-to-speech/text2speech.js` so the active tool consumes the engine audio module. +- Expanded targeted browser validation in `tests/playwright/tools/TextToSpeechFunctional.spec.mjs`. +- Updated Playwright V8 coverage artifacts for changed runtime JavaScript. + +## Requirement Checklist + +- PASS: Used `archive/v1-v2/tools/old_text2speech-V2/` as the required functionality sample. +- PASS: Reusable TTS behavior lives in `src/engine/audio/`. +- PASS: `toolbox/text-to-speech/` consumes `src/engine/audio/TextToSpeechEngine.js`. +- PASS: Restored old controls/options/features in the active tool. +- PASS: Browser SpeechSynthesis provider remains implemented. +- PASS: Planned provider adapters do not block browser preview behavior. +- PASS: No `tools/text2speech/` path was created. +- PASS: No database files were changed. +- PASS: Theme V2 remains the styling source. +- PASS: HTML uses external JavaScript only. +- PASS: No inline script, style, or event handler was added. + +## Restored Feature Set + +- Import JSON. +- Copy JSON. +- Export JSON. +- Project Workspace return action during workspace launch. +- Gender helper filter. +- Language filter. +- Browser voice selection. +- Voice details. +- Voice age shaping. +- Character presets. +- SSML-like presets. +- Volume, rate, and pitch sliders with visible values. +- Named sentence Name field. +- Add, Duplicate, and Delete named sentence actions. +- Text To Speak editor. +- Speak, Pause, Resume, and Stop playback actions. +- Named Sentences queue. +- Output Summary JSON. +- Clearable Status log. +- URL JSON preset loading through `samplePresetPath`. +- Project Workspace toolState loading and dirty-state writeback. + +## Validation Summary + +- PASS: `node --check src\engine\audio\TextToSpeechEngine.js` +- PASS: `node --check toolbox\text-to-speech\text2speech.js` +- PASS: `node --test tests\tools\Text2SpeechShell.test.mjs` +- PASS: `npx playwright test tests/playwright/tools/TextToSpeechFunctional.spec.mjs` +- PASS: `npm run test:workspace-v2` + - Note: command name is legacy; user-facing language is Project Workspace. +- PASS: `git diff --check` +- PASS: changed runtime JS coverage collected for `src/engine/audio/TextToSpeechEngine.js`. +- PASS: changed runtime JS coverage collected for `toolbox/text-to-speech/text2speech.js`. + +## Out Of Scope + +- No database changes. +- No external paid provider implementation. +- No generated audio file export provider. +- No new `tools/text2speech/` path. +- No archived tool activation. diff --git a/docs_build/dev/reports/PR_26171_061-validation.md b/docs_build/dev/reports/PR_26171_061-validation.md new file mode 100644 index 000000000..f8d339260 --- /dev/null +++ b/docs_build/dev/reports/PR_26171_061-validation.md @@ -0,0 +1,40 @@ +# PR_26171_061 Validation Report + +## Commands Run + +- `node --check src\engine\audio\TextToSpeechEngine.js` + - PASS. +- `node --check toolbox\text-to-speech\text2speech.js` + - PASS. +- `node --test tests\tools\Text2SpeechShell.test.mjs` + - PASS: 3 tests passed. +- `npx playwright test tests/playwright/tools/TextToSpeechFunctional.spec.mjs` + - PASS: 2 tests passed. + - Covers restored controls, preset shaping, queue add/duplicate/delete, output summary, pause/resume, speak, stop, and unavailable SpeechSynthesis error handling. +- `npm run test:workspace-v2` + - PASS: 5 Project Workspace tests passed. + - Note: command name is legacy; user-facing language is Project Workspace. + - Note: first execution used a 120s timeout and timed out before completion; rerun with a longer timeout completed successfully. +- `git diff --check` + - PASS. + +## Coverage + +- PASS: `docs_build/dev/reports/playwright_v8_coverage_report.txt` produced changed runtime JS coverage. +- PASS: `docs_build/dev/reports/coverage_changed_js_guardrail.txt` reports no changed runtime JS coverage warnings. +- PASS: `src/engine/audio/TextToSpeechEngine.js` covered at 71%. +- PASS: `toolbox/text-to-speech/text2speech.js` covered at 71%. + +## Artifact Verification + +- PASS: `docs_build/dev/reports/codex_review.diff` exists. +- PASS: `docs_build/dev/reports/codex_changed_files.txt` exists. +- PASS: `tmp/PR_26171_061-text2speech-engine-audio-feature-parity_delta.zip` exists. +- PASS: ZIP size is greater than zero. +- PASS: ZIP contents preserve repo-relative paths. + +## Skipped + +- Full samples validation skipped because no sample JSON or sample runtime behavior changed. +- Database validation skipped because no database files or runtime persistence changed. +- External provider validation skipped because paid/provider generation is out of scope and browser SpeechSynthesis is the implemented provider for this PR. diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index 4b9b70b93..c09df0df1 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,37 +1,65 @@ -# Codex Changed Files - PR_26171_063-codex-instruction-enforcement-hardening +# Codex Changed Files - PR_26171_061-text2speech-engine-audio-feature-parity -## Conflict Resolution Note -- Merged latest `origin/main` into the PR branch after GitHub reported merge conflicts. -- Regenerated Codex artifacts from the PR_063 delta against updated `origin/main`. -- No implementation scope was changed while resolving conflicts. +## Git Status Short +```text + A docs_build/dev/reports/PR_26171_061-engine-audio-ownership-checklist.md + A docs_build/dev/reports/PR_26171_061-instruction-compliance-checklist.md + A docs_build/dev/reports/PR_26171_061-manual-validation-notes.md + A docs_build/dev/reports/PR_26171_061-old-tts-feature-parity-checklist.md + A docs_build/dev/reports/PR_26171_061-text2speech-engine-audio-feature-parity.md + A docs_build/dev/reports/PR_26171_061-validation.md + M docs_build/dev/reports/codex_changed_files.txt + M docs_build/dev/reports/codex_review.diff + M docs_build/dev/reports/coverage_changed_js_guardrail.txt + M docs_build/dev/reports/playwright_v8_coverage_report.txt + M src/engine/audio/TextToSpeechEngine.js + M tests/playwright/tools/TextToSpeechFunctional.spec.mjs + M toolbox/text-to-speech/index.html + M toolbox/text-to-speech/text2speech.js +``` + +## Git Diff Stat +```text + ...R_26171_061-engine-audio-ownership-checklist.md | 25 + + ...R_26171_061-instruction-compliance-checklist.md | 32 + + .../PR_26171_061-manual-validation-notes.md | 27 + + ...R_26171_061-old-tts-feature-parity-checklist.md | 48 + + ..._061-text2speech-engine-audio-feature-parity.md | 71 + + docs_build/dev/reports/PR_26171_061-validation.md | 40 + + docs_build/dev/reports/codex_changed_files.txt | 82 +- + docs_build/dev/reports/codex_review.diff | 2057 +++++++++++++++++--- + .../dev/reports/coverage_changed_js_guardrail.txt | 8 +- + .../dev/reports/playwright_v8_coverage_report.txt | 37 +- + src/engine/audio/TextToSpeechEngine.js | 208 +- + .../tools/TextToSpeechFunctional.spec.mjs | 30 + + toolbox/text-to-speech/index.html | 119 +- + toolbox/text-to-speech/text2speech.js | 853 ++++++-- + 14 files changed, 3147 insertions(+), 490 deletions(-) +``` ## Changed Files -- docs_build/dev/PROJECT_INSTRUCTIONS.md -- docs_build/dev/PROJECT_MULTI_PC.txt -- docs_build/dev/reports/PR_26171_063-codex-instruction-enforcement-hardening.md -- docs_build/dev/reports/PR_26171_063-instruction-compliance-checklist.md -- docs_build/dev/reports/PR_26171_063-validation.md -- docs_build/dev/reports/PR_26171_063-manual-validation-notes.md +- src/engine/audio/TextToSpeechEngine.js +- toolbox/text-to-speech/index.html +- toolbox/text-to-speech/text2speech.js +- tests/playwright/tools/TextToSpeechFunctional.spec.mjs +- docs_build/dev/reports/coverage_changed_js_guardrail.txt +- docs_build/dev/reports/playwright_v8_coverage_report.txt +- docs_build/dev/reports/PR_26171_061-text2speech-engine-audio-feature-parity.md +- docs_build/dev/reports/PR_26171_061-instruction-compliance-checklist.md +- docs_build/dev/reports/PR_26171_061-old-tts-feature-parity-checklist.md +- docs_build/dev/reports/PR_26171_061-engine-audio-ownership-checklist.md +- docs_build/dev/reports/PR_26171_061-validation.md +- docs_build/dev/reports/PR_26171_061-manual-validation-notes.md - docs_build/dev/reports/codex_review.diff - docs_build/dev/reports/codex_changed_files.txt -## Git Diff Stat Against Updated origin/main -```text - docs_build/dev/PROJECT_INSTRUCTIONS.md | 47 ++ - docs_build/dev/PROJECT_MULTI_PC.txt | 58 ++ - ..._063-codex-instruction-enforcement-hardening.md | 48 ++ - ...R_26171_063-instruction-compliance-checklist.md | 37 ++ - .../PR_26171_063-manual-validation-notes.md | 19 + - docs_build/dev/reports/PR_26171_063-validation.md | 32 + - docs_build/dev/reports/codex_changed_files.txt | 48 ++ - docs_build/dev/reports/codex_review.diff | 699 +++++++++------------ - 8 files changed, 569 insertions(+), 419 deletions(-) -``` - ## Validation -- PASS: `git diff --check` after conflict resolution. -- PASS: targeted required instruction-anchor validation after conflict resolution. -- SKIP: Playwright, because the PR explicitly requires no Playwright and changes docs/workflow only. +- PASS: `node --check src\engine\audio\TextToSpeechEngine.js`. +- PASS: `node --check toolbox\text-to-speech\text2speech.js`. +- PASS: `node --test tests\tools\Text2SpeechShell.test.mjs`. +- PASS: `npx playwright test tests/playwright/tools/TextToSpeechFunctional.spec.mjs`. +- PASS: `npm run test:workspace-v2` (legacy command name; user-facing language is Project Workspace). +- PASS: `git diff --check`. ## ZIP -- Path: `tmp/PR_26171_063-codex-instruction-enforcement-hardening_delta.zip`. +- Path: `tmp/PR_26171_061-text2speech-engine-audio-feature-parity_delta.zip`. diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index ef70a97af..d8cb9dccc 100644 --- a/docs_build/dev/reports/codex_review.diff +++ b/docs_build/dev/reports/codex_review.diff @@ -1,281 +1,1856 @@ -diff --git a/docs_build/dev/PROJECT_INSTRUCTIONS.md b/docs_build/dev/PROJECT_INSTRUCTIONS.md -index 6a2014f2f..528312412 100644 ---- a/docs_build/dev/PROJECT_INSTRUCTIONS.md -+++ b/docs_build/dev/PROJECT_INSTRUCTIONS.md -@@ -2008,3 +2008,50 @@ Required Git workflow report fields: - - PR URL - - merge result - - final main commit -+ -+## CODEX INSTRUCTION ENFORCEMENT START GATE -+ -+Codex must run this gate before every PR execution and before any file changes. -+ -+Required instruction reads: -+- Read `docs_build/dev/PROJECT_INSTRUCTIONS.md`. -+- Read `docs_build/dev/PROJECT_MULTI_PC.txt`. -+- Treat the newest applicable section in `PROJECT_INSTRUCTIONS.md` as authoritative when rules overlap. -+- Treat the current owner/parity section in `PROJECT_MULTI_PC.txt` as authoritative for PC/Laptop routing. -+ -+Required pre-change report: -+- Codex must report instruction compliance as `PASS` or `FAIL` before making file changes. -+- The report must include branch, clean status, PR owner, PR parity, implementation path, validation scope, required report list, and ZIP requirement. -+- Any `FAIL` is a hard stop unless the PR explicitly scopes branch audit or recovery documentation without implementation. -+ -+Hard stops before changes: -+- If the current branch is not `main`, HARD STOP. -+- If the repository is not clean before the PR branch is created, HARD STOP. -+- If the PR owner does not match the PC/Laptop ownership map in `PROJECT_MULTI_PC.txt`, HARD STOP. -+- If the PR number parity does not match the assigned machine in `PROJECT_MULTI_PC.txt`, HARD STOP. -+- If the PR asks for implementation and the implementation path is wrong, HARD STOP. -+- If a PR asks for functional parity and only placeholder-only work is possible, HARD STOP and report the missing source or blocker. -+- If scoped validation is skipped without a documented reason, HARD STOP. -+ -+Path enforcement: -+- Use the active path named by the PR and verified in the repository. -+- Do not create wrong replacement paths. -+- For Text To Speech work, the active toolbox path is `toolbox/text-to-speech/`. -+- Do not create `tools/text2speech/` for new work. -+- If a PR names archived tools, games, or samples as a functionality sample, treat the archive as a read-only reference sample, not as a reason to skip implementation. -+ -+Completion hard stops: -+- If the required repo ZIP is missing, HARD STOP. -+- If the required repo ZIP is empty or not under `tmp/`, HARD STOP. -+- If required reports are missing, HARD STOP. -+- If `docs_build/dev/reports/codex_review.diff` is missing, HARD STOP. -+- If `docs_build/dev/reports/codex_changed_files.txt` is missing, HARD STOP. -+- If manual validation notes are missing, HARD STOP. -+- If the PR-specific report is missing, HARD STOP. -+- If an instruction compliance checklist is required and missing, HARD STOP. -+- If requested scoped validation did not run and no explicit skip reason is recorded, HARD STOP. -+ -+Functional parity rule: -+- A PR that asks to restore or rebuild working functionality must produce functional parity with the approved sample or source. -+- Placeholder shells, dead controls, static mockups, and nonfunctional UI are not acceptable completion states for functional parity PRs. -+- If functional parity cannot be reached in scope, Codex must stop and report the exact blocker instead of packaging placeholder-only work. -diff --git a/docs_build/dev/PROJECT_MULTI_PC.txt b/docs_build/dev/PROJECT_MULTI_PC.txt -index 3be408469..60ef4881a 100644 ---- a/docs_build/dev/PROJECT_MULTI_PC.txt -+++ b/docs_build/dev/PROJECT_MULTI_PC.txt -@@ -448,3 +448,61 @@ Queue next PR - ``` - - That is probably the fastest path to doubling throughput without creating chaos. -+ -+---------------------------------------------------------------------------------------- -+ -+Current Authoritative Multi-PC Gate -+ -+Codex must read this file before every PR execution. -+ -+Machine parity: -+ -+PC / Environment 1: -+- Uses even-numbered PR sequence values. -+- Example: `PR_26171_064-*`. -+ -+Laptop / Environment 2: -+- Uses odd-numbered PR sequence values. -+- Example: `PR_26171_063-*`. -+ -+Owner map: -+ -+PC / Environment 1 owns Creator Journey work: -+- Game Journey -+- Game Hub -+- Idea -+- Design -+- Objects -+- Worlds -+- Interface -+- Controls -+- Rules -+- Progression -+- Play Test -+- Progress Tracking -+- Game Design -+- Game Crew -+ -+Laptop / Environment 2 owns Content Creation and asset/publishing work: -+- Graphics -+- Toolbox images -+- Audio -+- MIDI -+- Messages -+- Text To Speech -+- TTS -+- Publishing -+- Marketplace -+- Community -+- Arcade -+ -+Governance, recovery, diagnostics, and instruction-hardening PRs: -+- Follow PR number parity unless Master Control explicitly assigns an owner. -+- Must not implement tool/runtime work from the opposite owner. -+- Must document owner/parity compliance in the PR report. -+ -+Hard stop rules: -+- If the PR number parity does not match the current machine, stop before changes. -+- If the PR scope belongs to the other machine owner, stop before changes. -+- If the PR crosses PC and Laptop ownership, stop and require Master Control to split or assign the work. -+- If the requested implementation path conflicts with the active owner path, stop before changes. -diff --git a/docs_build/dev/reports/PR_26171_063-codex-instruction-enforcement-hardening.md b/docs_build/dev/reports/PR_26171_063-codex-instruction-enforcement-hardening.md +diff --git a/docs_build/dev/reports/PR_26171_061-engine-audio-ownership-checklist.md b/docs_build/dev/reports/PR_26171_061-engine-audio-ownership-checklist.md new file mode 100644 -index 000000000..30bcba263 +index 000000000..5e18e0955 --- /dev/null -+++ b/docs_build/dev/reports/PR_26171_063-codex-instruction-enforcement-hardening.md -@@ -0,0 +1,48 @@ -+# PR_26171_063 Codex Instruction Enforcement Hardening ++++ b/docs_build/dev/reports/PR_26171_061-engine-audio-ownership-checklist.md +@@ -0,0 +1,25 @@ ++# PR_26171_061 Engine Audio Ownership Checklist + -+## Purpose ++## Engine Ownership + -+Make Codex explicitly obey `docs_build/dev/PROJECT_INSTRUCTIONS.md` and `docs_build/dev/PROJECT_MULTI_PC.txt` before every PR. ++- PASS: Reusable TTS voice filtering lives in `src/engine/audio/TextToSpeechEngine.js`. ++- PASS: Reusable TTS preset shaping lives in `src/engine/audio/TextToSpeechEngine.js`. ++- PASS: Reusable TTS queue item helpers live in `src/engine/audio/TextToSpeechEngine.js`. ++- PASS: Existing defaults remain in `src/engine/audio/TextToSpeechDefaults.js`. ++- PASS: Active toolbox UI consumes the engine module instead of duplicating all engine behavior locally. + -+## Scope ++## Active Tool Ownership + -+- Added a Codex instruction enforcement start gate to `docs_build/dev/PROJECT_INSTRUCTIONS.md`. -+- Added current authoritative PC/Laptop owner and PR parity rules to `docs_build/dev/PROJECT_MULTI_PC.txt`. -+- Documented hard stops for branch state, clean status, owner mismatch, parity mismatch, wrong implementation paths, missing reports, missing ZIPs, skipped validation, and placeholder-only functional parity work. ++- PASS: Active tool remains under `toolbox/text-to-speech/`. ++- PASS: Active HTML remains Theme V2 only. ++- PASS: Active JavaScript remains external. ++- PASS: Active tool owns DOM wiring, status display, and control event handling. ++- PASS: Active tool does not create database behavior. + -+## Requirement Checklist ++## Boundary Checks + -+- PASS: Codex must read `docs_build/dev/PROJECT_INSTRUCTIONS.md` before execution. -+- PASS: Codex must read `docs_build/dev/PROJECT_MULTI_PC.txt` before execution. -+- PASS: Codex must report instruction compliance PASS/FAIL before changes. -+- PASS: Codex must hard stop when not on `main` before starting. -+- PASS: Codex must hard stop when the repo is not clean before starting. -+- PASS: Codex must hard stop when PR owner does not match PC/Laptop ownership. -+- PASS: Codex must hard stop when PR number parity does not match assigned machine. -+- PASS: Codex must hard stop when required ZIP is missing. -+- PASS: Codex must hard stop when required reports are missing. -+- PASS: Codex must hard stop when implementation path is wrong. -+- PASS: Codex must hard stop when scoped validation was skipped without reason. -+- PASS: Codex must not continue with placeholder-only work when the PR asks for functional parity. -+- PASS: Codex must not create wrong paths such as `tools/text2speech` when active path is `toolbox/text-to-speech`. -+- PASS: Codex must not treat archived tools as "do not implement" when the PR says they are the functionality sample. -+ -+## Owner And Parity Evidence -+ -+- Current PR: `PR_26171_063-codex-instruction-enforcement-hardening`. -+- Sequence: `063`. -+- Sequence parity: odd. -+- Assigned machine under the new authoritative gate: Laptop / Environment 2. -+- Scope type: governance, recovery, diagnostics, and instruction-hardening. -+- Result: PASS because governance/instruction-hardening PRs follow PR number parity unless Master Control explicitly assigns another owner. -+ -+## Validation Scope -+ -+- Playwright impacted: No. -+- No Playwright impact. This PR is docs/workflow only. -+- Validation is docs/static only. -+- Runtime, tool, engine, samples, and Game Hub validation are skipped because no runtime, UI, toolState, engine, or sample behavior changed. -+ -+## ZIP -+ -+- Required ZIP path: `tmp/PR_26171_063-codex-instruction-enforcement-hardening_delta.zip`. -diff --git a/docs_build/dev/reports/PR_26171_063-instruction-compliance-checklist.md b/docs_build/dev/reports/PR_26171_063-instruction-compliance-checklist.md ++- PASS: No `tools/text2speech/` path was created. ++- PASS: Archived `old_text2speech-V2` was used as a read-only functionality sample. ++- PASS: Archived tool files were not activated in navigation. ++- PASS: Browser SpeechSynthesis is the implemented local provider. ++- PASS: Planned providers remain documented but do not block browser preview. +diff --git a/docs_build/dev/reports/PR_26171_061-instruction-compliance-checklist.md b/docs_build/dev/reports/PR_26171_061-instruction-compliance-checklist.md new file mode 100644 -index 000000000..9d2277ab5 +index 000000000..ffb2cb288 --- /dev/null -+++ b/docs_build/dev/reports/PR_26171_063-instruction-compliance-checklist.md -@@ -0,0 +1,37 @@ -+# PR_26171_063 Instruction Compliance Checklist ++++ b/docs_build/dev/reports/PR_26171_061-instruction-compliance-checklist.md +@@ -0,0 +1,32 @@ ++# PR_26171_061 Instruction Compliance Checklist + -+## Pre-Change Gate ++## Pre-Work Gate + +- PASS: Read `docs_build/dev/PROJECT_INSTRUCTIONS.md` before file changes. +- PASS: Read `docs_build/dev/PROJECT_MULTI_PC.txt` before file changes. +- PASS: Reported instruction compliance PASS/FAIL before file changes. -+- PASS: Starting branch was `main`. -+- PASS: Starting repository status was clean. -+- PASS: Created scoped branch `pr/26171-063-codex-instruction-enforcement-hardening` after the clean `main` check. -+ -+## Current PR Routing -+ -+- PASS: PR number `063` is odd. -+- PASS: Odd PR parity maps to Laptop / Environment 2 under the new authoritative Multi-PC gate. -+- PASS: PR scope is instruction/governance hardening and follows parity because no Master Control owner override was provided. -+- PASS: No PC-owned tool implementation was changed. -+- PASS: No Laptop-owned tool implementation was changed. -+ -+## Path And Functional Parity Gate -+ -+- PASS: No implementation path was changed by this docs-only PR. -+- PASS: `toolbox/text-to-speech/` is documented as the active Text To Speech path. -+- PASS: `tools/text2speech/` is documented as a wrong new-work path. -+- PASS: Placeholder-only functional parity work is prohibited. -+- PASS: Archived functionality samples are documented as read-only reference samples when explicitly named by a PR. -+ -+## Completion Gate -+ -+- PASS: Required PR-specific report exists. -+- PASS: Required instruction compliance checklist exists. -+- PASS: Required manual validation notes exist. -+- PASS: Required `docs_build/dev/reports/codex_review.diff` exists after artifact generation. -+- PASS: Required `docs_build/dev/reports/codex_changed_files.txt` exists after artifact generation. -+- PASS: Required ZIP exists after packaging. -+- PASS: Scoped validation was not skipped; docs/static validation was run. -+- PASS: Playwright was not run because the PR explicitly requires no Playwright. -diff --git a/docs_build/dev/reports/PR_26171_063-manual-validation-notes.md b/docs_build/dev/reports/PR_26171_063-manual-validation-notes.md ++- PASS: `git branch --show-current` reported `main` before branch creation. ++- PASS: `git checkout main` completed before branch creation. ++- PASS: `git pull origin main` completed before branch creation. ++- PASS: `git status` reported a clean working tree before branch creation. ++- PASS: Created branch `pr/26171-061-text2speech-engine-audio-feature-parity` from clean latest `main`. ++ ++## Owner And Parity ++ ++- PASS: PR number `061` is odd. ++- PASS: Odd parity maps to Laptop / Environment 2. ++- PASS: Text To Speech / TTS is owned by Laptop / Environment 2. ++- PASS: Implementation path matches active owner path: `toolbox/text-to-speech/`. ++- PASS: Shared engine path matches approved reusable audio path: `src/engine/audio/`. ++ ++## Hard Stop Checks ++ ++- PASS: Instructions were present and readable. ++- PASS: Branch workflow could be followed. ++- PASS: Repository was clean before branching. ++- PASS: No owner mismatch. ++- PASS: No parity mismatch. ++- PASS: No wrong implementation path. ++- PASS: Scoped validation was not skipped. ++- PASS: Required reports were created. ++- PASS: Required repo ZIP was created. +diff --git a/docs_build/dev/reports/PR_26171_061-manual-validation-notes.md b/docs_build/dev/reports/PR_26171_061-manual-validation-notes.md new file mode 100644 -index 000000000..43af53cc8 +index 000000000..3341b14ba --- /dev/null -+++ b/docs_build/dev/reports/PR_26171_063-manual-validation-notes.md -@@ -0,0 +1,19 @@ -+# PR_26171_063 Manual Validation Notes ++++ b/docs_build/dev/reports/PR_26171_061-manual-validation-notes.md +@@ -0,0 +1,27 @@ ++# PR_26171_061 Manual Validation Notes + +## Manual Review + -+- Confirmed the new start gate requires reading `PROJECT_INSTRUCTIONS.md` and `PROJECT_MULTI_PC.txt` before every PR. -+- Confirmed the new gate requires visible PASS/FAIL instruction compliance before file changes. -+- Confirmed the hard stop list covers branch, clean status, owner, parity, required ZIP, required reports, wrong paths, skipped validation, and placeholder-only functional parity work. -+- Confirmed Text To Speech path enforcement names `toolbox/text-to-speech/` as active and rejects new `tools/text2speech/` work. -+- Confirmed archived tools can be used as read-only functionality samples when a PR explicitly names them. ++- Confirmed active Text To Speech path is `toolbox/text-to-speech/`. ++- Confirmed no `tools/text2speech/` directory was created. ++- Confirmed old functionality sample path is `archive/v1-v2/tools/old_text2speech-V2/`. ++- Confirmed active HTML has external scripts only. ++- Confirmed active HTML has no inline style attributes or inline event handlers. ++- Confirmed no database files were changed. ++ ++## Automated Browser Coverage Used For Manual Equivalents + -+## Manual Runtime Checks ++- Opened active Text To Speech page through targeted Playwright validation. ++- Verified browser voices render. ++- Verified restored control groups render. ++- Verified preset shaping updates pitch and volume values. ++- Verified named sentence Add, Duplicate, and Delete. ++- Verified output summary includes queue JSON. ++- Verified Speak, Pause, Resume, and Stop call the browser SpeechSynthesis mock. ++- Verified unavailable SpeechSynthesis shows actionable error. + -+- No Playwright was run. -+- No browser runtime checks were performed. -+- No Project Workspace/Game Hub runtime checks were performed. -+- No Local API checks were performed. -+- No Text To Speech runtime checks were performed. ++## Out Of Scope Manual Checks + -+These checks are out of scope because this PR only changes documentation and governance reports. -diff --git a/docs_build/dev/reports/PR_26171_063-validation.md b/docs_build/dev/reports/PR_26171_063-validation.md ++- No paid provider was manually exercised. ++- No generated audio file export was manually exercised. ++- No database behavior was manually exercised. +diff --git a/docs_build/dev/reports/PR_26171_061-old-tts-feature-parity-checklist.md b/docs_build/dev/reports/PR_26171_061-old-tts-feature-parity-checklist.md new file mode 100644 -index 000000000..cedb0134a +index 000000000..e09e0f974 --- /dev/null -+++ b/docs_build/dev/reports/PR_26171_063-validation.md -@@ -0,0 +1,32 @@ -+# PR_26171_063 Validation ++++ b/docs_build/dev/reports/PR_26171_061-old-tts-feature-parity-checklist.md +@@ -0,0 +1,48 @@ ++# PR_26171_061 Old TTS Feature Parity Checklist + -+## Scope ++Functionality sample: `archive/v1-v2/tools/old_text2speech-V2/` + -+Docs/static validation only. ++## Controls And Options + -+No Playwright was run because the PR explicitly says "No Playwright" and the changed files are governance docs and reports only. ++- PASS: Gender helper filter restored. ++- PASS: Language filter restored. ++- PASS: Voice dropdown restored. ++- PASS: Voice details restored. ++- PASS: Voice Age select restored. ++- PASS: Character Preset select restored. ++- PASS: SSML-like Preset select restored. ++- PASS: Volume slider restored with visible value. ++- PASS: Rate / Speed slider restored with visible value. ++- PASS: Pitch slider restored with visible value. ++- PASS: Name field restored. ++- PASS: Text To Speak editor restored. + -+## Commands ++## Queue And JSON + -+- `git diff --check` -+ - PASS: no whitespace errors. -+- Targeted required-text validation for `docs_build/dev/PROJECT_INSTRUCTIONS.md`, `docs_build/dev/PROJECT_MULTI_PC.txt`, and PR_26171_063 reports. -+ - PASS: all required hardening anchors were found. -+ - Note: the first targeted text check found missing exact `No Playwright` wording in manual notes; the note was corrected and the validation was rerun successfully. ++- PASS: Named Sentences queue restored. ++- PASS: Add named sentence restored. ++- PASS: Duplicate named sentence restored. ++- PASS: Delete named sentence restored. ++- PASS: Output Summary JSON restored. ++- PASS: Import JSON restored for standalone launch. ++- PASS: Copy JSON restored for standalone launch. ++- PASS: Export JSON restored for standalone launch. ++- PASS: URL JSON source loading restored through `samplePresetPath`. + -+## Skipped ++## Playback And Status ++ ++- PASS: Speak restored. ++- PASS: Pause restored when browser support exists. ++- PASS: Resume restored when browser support exists. ++- PASS: Stop restored. ++- PASS: Clearable status log restored. ++- PASS: Actionable unavailable-browser error restored. ++- PASS: No placeholder provider-blocking behavior remains for browser preview. ++ ++## Workspace ++ ++- PASS: Project Workspace launch detection restored. ++- PASS: Standalone JSON actions hide during Project Workspace launch. ++- PASS: Return to Project Workspace action restored. ++- PASS: Project Workspace toolState loading restored. ++- PASS: Project Workspace dirty-state writeback restored. +diff --git a/docs_build/dev/reports/PR_26171_061-text2speech-engine-audio-feature-parity.md b/docs_build/dev/reports/PR_26171_061-text2speech-engine-audio-feature-parity.md +new file mode 100644 +index 000000000..b2f4a44d3 +--- /dev/null ++++ b/docs_build/dev/reports/PR_26171_061-text2speech-engine-audio-feature-parity.md +@@ -0,0 +1,71 @@ ++# PR_26171_061 Text To Speech Engine Audio Feature Parity ++ ++## Summary ++ ++Restored active Text To Speech feature parity from the archived `old_text2speech-V2` functionality sample while keeping active implementation in `toolbox/text-to-speech/` and reusable engine behavior in `src/engine/audio/`. ++ ++## Changed Scope ++ ++- Rebuilt reusable Text To Speech voice filtering, preset shaping, and queue-item helpers in `src/engine/audio/TextToSpeechEngine.js`. ++- Expanded `toolbox/text-to-speech/index.html` with Theme V2 controls for the old V2 feature set. ++- Rebuilt `toolbox/text-to-speech/text2speech.js` so the active tool consumes the engine audio module. ++- Expanded targeted browser validation in `tests/playwright/tools/TextToSpeechFunctional.spec.mjs`. ++- Updated Playwright V8 coverage artifacts for changed runtime JavaScript. ++ ++## Requirement Checklist ++ ++- PASS: Used `archive/v1-v2/tools/old_text2speech-V2/` as the required functionality sample. ++- PASS: Reusable TTS behavior lives in `src/engine/audio/`. ++- PASS: `toolbox/text-to-speech/` consumes `src/engine/audio/TextToSpeechEngine.js`. ++- PASS: Restored old controls/options/features in the active tool. ++- PASS: Browser SpeechSynthesis provider remains implemented. ++- PASS: Planned provider adapters do not block browser preview behavior. ++- PASS: No `tools/text2speech/` path was created. ++- PASS: No database files were changed. ++- PASS: Theme V2 remains the styling source. ++- PASS: HTML uses external JavaScript only. ++- PASS: No inline script, style, or event handler was added. ++ ++## Restored Feature Set ++ ++- Import JSON. ++- Copy JSON. ++- Export JSON. ++- Project Workspace return action during workspace launch. ++- Gender helper filter. ++- Language filter. ++- Browser voice selection. ++- Voice details. ++- Voice age shaping. ++- Character presets. ++- SSML-like presets. ++- Volume, rate, and pitch sliders with visible values. ++- Named sentence Name field. ++- Add, Duplicate, and Delete named sentence actions. ++- Text To Speak editor. ++- Speak, Pause, Resume, and Stop playback actions. ++- Named Sentences queue. ++- Output Summary JSON. ++- Clearable Status log. ++- URL JSON preset loading through `samplePresetPath`. ++- Project Workspace toolState loading and dirty-state writeback. ++ ++## Validation Summary ++ ++- PASS: `node --check src\engine\audio\TextToSpeechEngine.js` ++- PASS: `node --check toolbox\text-to-speech\text2speech.js` ++- PASS: `node --test tests\tools\Text2SpeechShell.test.mjs` ++- PASS: `npx playwright test tests/playwright/tools/TextToSpeechFunctional.spec.mjs` ++- PASS: `npm run test:workspace-v2` ++ - Note: command name is legacy; user-facing language is Project Workspace. ++- PASS: `git diff --check` ++- PASS: changed runtime JS coverage collected for `src/engine/audio/TextToSpeechEngine.js`. ++- PASS: changed runtime JS coverage collected for `toolbox/text-to-speech/text2speech.js`. ++ ++## Out Of Scope ++ ++- No database changes. ++- No external paid provider implementation. ++- No generated audio file export provider. ++- No new `tools/text2speech/` path. ++- No archived tool activation. +diff --git a/docs_build/dev/reports/PR_26171_061-validation.md b/docs_build/dev/reports/PR_26171_061-validation.md +new file mode 100644 +index 000000000..f8d339260 +--- /dev/null ++++ b/docs_build/dev/reports/PR_26171_061-validation.md +@@ -0,0 +1,40 @@ ++# PR_26171_061 Validation Report + ++## Commands Run ++ ++- `node --check src\engine\audio\TextToSpeechEngine.js` ++ - PASS. ++- `node --check toolbox\text-to-speech\text2speech.js` ++ - PASS. ++- `node --test tests\tools\Text2SpeechShell.test.mjs` ++ - PASS: 3 tests passed. ++- `npx playwright test tests/playwright/tools/TextToSpeechFunctional.spec.mjs` ++ - PASS: 2 tests passed. ++ - Covers restored controls, preset shaping, queue add/duplicate/delete, output summary, pause/resume, speak, stop, and unavailable SpeechSynthesis error handling. +- `npm run test:workspace-v2` -+ - SKIP: legacy command name for Project Workspace/Game Hub validation; no workspace or toolState behavior changed. -+- `npm run test:playwright:static` -+ - SKIP: command name is Playwright-scoped and this PR explicitly says no Playwright. -+- Runtime, tool, engine, samples, and browser validation -+ - SKIP: docs/workflow-only change. ++ - PASS: 5 Project Workspace tests passed. ++ - Note: command name is legacy; user-facing language is Project Workspace. ++ - Note: first execution used a 120s timeout and timed out before completion; rerun with a longer timeout completed successfully. ++- `git diff --check` ++ - PASS. ++ ++## Coverage ++ ++- PASS: `docs_build/dev/reports/playwright_v8_coverage_report.txt` produced changed runtime JS coverage. ++- PASS: `docs_build/dev/reports/coverage_changed_js_guardrail.txt` reports no changed runtime JS coverage warnings. ++- PASS: `src/engine/audio/TextToSpeechEngine.js` covered at 71%. ++- PASS: `toolbox/text-to-speech/text2speech.js` covered at 71%. + +## Artifact Verification + +- PASS: `docs_build/dev/reports/codex_review.diff` exists. +- PASS: `docs_build/dev/reports/codex_changed_files.txt` exists. -+- PASS: `tmp/PR_26171_063-codex-instruction-enforcement-hardening_delta.zip` exists. ++- PASS: `tmp/PR_26171_061-text2speech-engine-audio-feature-parity_delta.zip` exists. +- PASS: ZIP size is greater than zero. +- PASS: ZIP contents preserve repo-relative paths. ++ ++## Skipped ++ ++- Full samples validation skipped because no sample JSON or sample runtime behavior changed. ++- Database validation skipped because no database files or runtime persistence changed. ++- External provider validation skipped because paid/provider generation is out of scope and browser SpeechSynthesis is the implemented provider for this PR. +diff --git a/docs_build/dev/reports/coverage_changed_js_guardrail.txt b/docs_build/dev/reports/coverage_changed_js_guardrail.txt +index 076d27690..807990e9f 100644 +--- a/docs_build/dev/reports/coverage_changed_js_guardrail.txt ++++ b/docs_build/dev/reports/coverage_changed_js_guardrail.txt +@@ -6,10 +6,8 @@ Missing changed runtime JS files are WARN, not FAIL. + Source: Playwright/Chromium built-in V8 coverage from the active Playwright run. + + Changed runtime JS files considered: +-(0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only +-(0%) src/shared/toolbox/tool-metadata-inventory.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only +-(70%) toolbox/idea-board/index.js - executed lines 480/480; executed functions 23/33 ++(71%) src/engine/audio/TextToSpeechEngine.js - executed lines 412/412; executed functions 37/52 ++(71%) toolbox/text-to-speech/text2speech.js - executed lines 835/835; executed functions 61/86 + + Guardrail warnings: +-(0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file missing from coverage; advisory only +-(0%) src/shared/toolbox/tool-metadata-inventory.js - WARNING: changed runtime JS file missing from coverage; advisory only ++(100%) none - no changed runtime JS coverage warnings +diff --git a/docs_build/dev/reports/playwright_v8_coverage_report.txt b/docs_build/dev/reports/playwright_v8_coverage_report.txt +index cb35b4d50..dd573eabd 100644 +--- a/docs_build/dev/reports/playwright_v8_coverage_report.txt ++++ b/docs_build/dev/reports/playwright_v8_coverage_report.txt +@@ -12,35 +12,28 @@ Note: entry percentages use function coverage when available, otherwise line cov + Note: coverage entries are aggregated across every page/tool where coverageReporter.start(page) and coverageReporter.stop(page) ran. + + Exercised tool entry points detected: +-(78%) Toolbox Index - exercised 4 runtime JS files ++(72%) Toolbox Index - exercised 2 runtime JS files + (0%) Tool Template V2 - not exercised by this Playwright run +-(63%) Theme V2 Shared JS - exercised 2 runtime JS files ++(56%) Theme V2 Shared JS - exercised 2 runtime JS files + + Changed runtime JS files covered: +-(0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only +-(0%) src/shared/toolbox/tool-metadata-inventory.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only +-(70%) toolbox/idea-board/index.js - executed lines 480/480; executed functions 23/33 ++(71%) src/engine/audio/TextToSpeechEngine.js - executed lines 412/412; executed functions 37/52 ++(71%) toolbox/text-to-speech/text2speech.js - executed lines 835/835; executed functions 61/86 + + Files with executed line/function counts where available: +-(25%) src/api/session-api-client.js - executed lines 68/68; executed functions 3/12 +-(33%) src/api/toolbox-votes-api-client.js - executed lines 46/46; executed functions 2/6 +-(44%) src/api/server-api-client.js - executed lines 167/167; executed functions 8/18 +-(63%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 977/977; executed functions 54/86 ++(36%) src/api/server-api-client.js - executed lines 167/167; executed functions 5/14 ++(38%) src/api/public-config-client.js - executed lines 209/209; executed functions 10/26 ++(54%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 977/977; executed functions 46/85 + (64%) assets/theme-v2/js/tool-display-mode.js - executed lines 209/209; executed functions 9/14 +-(65%) src/api/public-config-client.js - executed lines 209/209; executed functions 17/26 +-(67%) src/api/game-journey-completion-api-client.js - executed lines 15/15; executed functions 2/3 +-(67%) toolbox/game-workspace/game-workspace-api-client.js - executed lines 20/20; executed functions 2/3 +-(70%) toolbox/idea-board/index.js - executed lines 480/480; executed functions 23/33 +-(78%) toolbox/tools-page-accordions.js - executed lines 1156/1156; executed functions 87/111 +-(86%) toolbox/tool-registry-api-client.js - executed lines 155/155; executed functions 25/29 ++(71%) src/engine/audio/TextToSpeechEngine.js - executed lines 412/412; executed functions 37/52 ++(71%) toolbox/text-to-speech/text2speech.js - executed lines 835/835; executed functions 61/86 ++(76%) toolbox/tool-registry-api-client.js - executed lines 155/155; executed functions 22/29 ++(100%) src/engine/audio/TextToSpeechDefaults.js - executed lines 108/108; executed functions 1/1 + + Uncovered or low-coverage changed JS files: +-(0%) src/dev-runtime/server/local-api-router.mjs - WARNING: uncovered changed runtime JS file; advisory only +-(0%) src/shared/toolbox/tool-metadata-inventory.js - WARNING: uncovered changed runtime JS file; advisory only ++(100%) none - no low-coverage changed runtime JS files + + Changed JS files considered: +-(0%) src/dev-runtime/server/local-api-router.mjs - changed JS file not collected as browser runtime coverage +-(0%) src/shared/toolbox/tool-metadata-inventory.js - changed JS file not collected as browser runtime coverage +-(0%) tests/playwright/tools/IdeaBoardTableNotes.spec.mjs - changed JS file not collected as browser runtime coverage +-(0%) tests/playwright/tools/ToolboxRoutePages.spec.mjs - changed JS file not collected as browser runtime coverage +-(70%) toolbox/idea-board/index.js - changed JS file with browser V8 coverage ++(0%) tests/playwright/tools/TextToSpeechFunctional.spec.mjs - changed JS file not collected as browser runtime coverage ++(71%) src/engine/audio/TextToSpeechEngine.js - changed JS file with browser V8 coverage ++(71%) toolbox/text-to-speech/text2speech.js - changed JS file with browser V8 coverage +diff --git a/src/engine/audio/TextToSpeechEngine.js b/src/engine/audio/TextToSpeechEngine.js +index c8e1dced3..a5f3ccabd 100644 +--- a/src/engine/audio/TextToSpeechEngine.js ++++ b/src/engine/audio/TextToSpeechEngine.js +@@ -1,7 +1,10 @@ + import { ++ TEXT_TO_SPEECH_CHARACTER_PRESET_DEFAULTS, + TEXT_TO_SPEECH_DEFAULTS, + TEXT_TO_SPEECH_DISPLAY_NAME, +- TEXT_TO_SPEECH_RANGE_DEFAULTS ++ TEXT_TO_SPEECH_RANGE_DEFAULTS, ++ TEXT_TO_SPEECH_SSML_LIKE_PRESET_DEFAULTS, ++ TEXT_TO_SPEECH_VOICE_AGE_PRESET_DEFAULTS + } from "./TextToSpeechDefaults.js"; + + function finiteNumber(value, fallback) { +@@ -9,8 +12,180 @@ function finiteNumber(value, fallback) { + return Number.isFinite(number) ? number : fallback; + } + +-function boundedNumber(value, { fallback, max, min }) { +- return Math.min(max, Math.max(min, finiteNumber(value, fallback))); ++function boundedNumber(value, { fallback, max, min, value: defaultValue }) { ++ const fallbackValue = fallback ?? defaultValue ?? min; ++ return Math.min(max, Math.max(min, finiteNumber(value, fallbackValue))); ++} ++ ++function shapedNumber(value, { fallback, max, min, step }) { ++ const boundedValue = boundedNumber(value, { fallback, max, min }); ++ const stepText = String(step || "1"); ++ const decimalPart = stepText.includes(".") ? stepText.split(".")[1] : ""; ++ const multiplier = 10 ** decimalPart.length; ++ return Math.round(boundedValue * multiplier) / multiplier; ++} ++ ++function textToSpeechSlugFromName(name) { ++ const slug = String(name || "") ++ .trim() ++ .toLowerCase() ++ .replace(/[^a-z0-9]+/g, "-") ++ .replace(/^-+|-+$/g, ""); ++ return slug || "speech-item"; ++} ++ ++function textToSpeechVoiceGender(option) { ++ const voiceText = `${option?.gender || ""} ${option?.name || ""} ${option?.label || ""}`; ++ const voiceLanguage = String(option?.language || "").toLowerCase(); ++ if (/\bneutral\b|\bnon[-\s]?binary\b|\bandrogynous\b/i.test(voiceText)) return "neutral"; ++ if (voiceLanguage === "es-es" || /\bmale\b|\bman\b|\bdavid\b|\bmark\b/i.test(voiceText)) return "male"; ++ if (/\bfemale\b|\bwoman\b|\bzira\b/i.test(voiceText)) return "female"; ++ return "unknown"; ++} ++ ++function genderFilterLabel(value) { ++ if (value === "male-preferred") return "Male"; ++ if (value === "female-preferred") return "Female"; ++ if (value === "neutral") return "Neutral"; ++ return "Any"; ++} ++ ++function ageFilterLabel(value) { ++ if (value === "adult") return "Adult"; ++ if (value === "child") return "Child"; ++ if (value === "elderly") return "Elderly"; ++ if (value === "teen") return "Teen"; ++ return "Any"; ++} ++ ++function payloadGenderValue(value) { ++ return value === "neutral" ? "any" : value; ++} ++ ++function filterTextToSpeechVoicesByGender(voiceOptions, genderFilter = "any") { ++ if (genderFilter === "any") return voiceOptions; ++ if (genderFilter === "male-preferred") { ++ return voiceOptions.filter((option) => textToSpeechVoiceGender(option) === "male"); ++ } ++ if (genderFilter === "female-preferred") { ++ return voiceOptions.filter((option) => textToSpeechVoiceGender(option) === "female"); ++ } ++ if (genderFilter === "neutral") { ++ return voiceOptions.filter((option) => ["neutral", "unknown"].includes(textToSpeechVoiceGender(option))); ++ } ++ return voiceOptions; ++} ++ ++function optionLabelCompare(left, right) { ++ return String(left.label).localeCompare(String(right.label), undefined, { ++ numeric: true, ++ sensitivity: "base" ++ }); ++} ++ ++function textToSpeechLanguageOptionsFromVoices(voiceOptions) { ++ const languageCounts = new Map(); ++ voiceOptions.forEach((option) => { ++ const language = String(option.language || "").trim(); ++ if (language) { ++ languageCounts.set(language, (languageCounts.get(language) || 0) + 1); ++ } ++ }); ++ return Array.from(languageCounts.entries()) ++ .map(([language, count]) => ({ ++ label: `${language} (${count} ${count === 1 ? "voice" : "voices"})`, ++ value: language ++ })) ++ .sort(optionLabelCompare); ++} ++ ++function filterTextToSpeechVoiceOptions(voiceOptions, { gender = "any", language = "" } = {}) { ++ const filteredByGender = filterTextToSpeechVoicesByGender(voiceOptions, gender); ++ const matchingVoices = filteredByGender ++ .filter((option) => !language || option.language === language) ++ .sort(optionLabelCompare); ++ return { ++ filteredVoiceCount: filteredByGender.length, ++ gender, ++ genderLabel: genderFilterLabel(gender), ++ language, ++ languageOptions: textToSpeechLanguageOptionsFromVoices(filteredByGender), ++ matchingVoices, ++ voiceCount: voiceOptions.length ++ }; ++} ++ ++function shapeTextToSpeechOptions({ ++ characterPreset = TEXT_TO_SPEECH_DEFAULTS.characterPreset, ++ pitch = TEXT_TO_SPEECH_DEFAULTS.pitch, ++ rate = TEXT_TO_SPEECH_DEFAULTS.rate, ++ ssmlLikePreset = TEXT_TO_SPEECH_DEFAULTS.ssmlLikePreset, ++ voiceAge = TEXT_TO_SPEECH_DEFAULTS.voiceAge, ++ volume = TEXT_TO_SPEECH_DEFAULTS.volume ++} = {}, sliderOverrides = {}) { ++ const character = TEXT_TO_SPEECH_CHARACTER_PRESET_DEFAULTS[characterPreset] || TEXT_TO_SPEECH_CHARACTER_PRESET_DEFAULTS.manual; ++ const age = TEXT_TO_SPEECH_VOICE_AGE_PRESET_DEFAULTS[voiceAge] || TEXT_TO_SPEECH_VOICE_AGE_PRESET_DEFAULTS.any; ++ const ssmlLike = TEXT_TO_SPEECH_SSML_LIKE_PRESET_DEFAULTS[ssmlLikePreset] || TEXT_TO_SPEECH_SSML_LIKE_PRESET_DEFAULTS.normal; ++ return { ++ characterPreset, ++ pitch: sliderOverrides.pitch ++ ? boundedNumber(pitch, TEXT_TO_SPEECH_RANGE_DEFAULTS.pitch) ++ : shapedNumber(Number(character.pitch) + Number(age.pitchOffset) + Number(ssmlLike.pitchOffset), TEXT_TO_SPEECH_RANGE_DEFAULTS.pitch), ++ rate: sliderOverrides.rate ++ ? boundedNumber(rate, TEXT_TO_SPEECH_RANGE_DEFAULTS.rate) ++ : shapedNumber(Number(character.rate) * Number(age.rateMultiplier) * Number(ssmlLike.rateMultiplier), TEXT_TO_SPEECH_RANGE_DEFAULTS.rate), ++ ssmlLikePreset, ++ voiceAge, ++ volume: sliderOverrides.volume ++ ? boundedNumber(volume, TEXT_TO_SPEECH_RANGE_DEFAULTS.volume) ++ : shapedNumber(Number(character.volume) * Number(ssmlLike.volumeMultiplier), TEXT_TO_SPEECH_RANGE_DEFAULTS.volume) ++ }; ++} ++ ++function uniqueTextToSpeechName(baseName, existingItems = []) { ++ const requestedName = String(baseName || "").trim() || "New speech item"; ++ const existingNames = new Set(existingItems.map((item) => item.name)); ++ if (!existingNames.has(requestedName)) return requestedName; ++ for (let index = 2; index < 1000; index += 1) { ++ const candidate = `${requestedName} ${index}`; ++ if (!existingNames.has(candidate)) return candidate; ++ } ++ return `${requestedName} ${Date.now().toString(36)}`; ++} ++ ++function uniqueTextToSpeechId(baseName, existingItems = []) { ++ const existingIds = new Set(existingItems.map((item) => item.id)); ++ const baseId = textToSpeechSlugFromName(baseName); ++ if (!existingIds.has(baseId)) return baseId; ++ for (let index = 2; index < 1000; index += 1) { ++ const candidate = `${baseId}-${index}`; ++ if (!existingIds.has(candidate)) return candidate; ++ } ++ return `${baseId}-${Date.now().toString(36)}`; ++} ++ ++function createTextToSpeechQueueItem({ ++ existingItems = [], ++ id = "", ++ name = "", ++ text = "", ++ ...options ++} = {}) { ++ const itemName = uniqueTextToSpeechName(name, id ? [] : existingItems); ++ return { ++ characterPreset: options.characterPreset || TEXT_TO_SPEECH_DEFAULTS.characterPreset, ++ gender: payloadGenderValue(options.gender || TEXT_TO_SPEECH_DEFAULTS.gender), ++ id: id || uniqueTextToSpeechId(itemName, existingItems), ++ language: options.language || TEXT_TO_SPEECH_DEFAULTS.language, ++ name: itemName, ++ pitch: boundedNumber(options.pitch, TEXT_TO_SPEECH_RANGE_DEFAULTS.pitch), ++ rate: boundedNumber(options.rate, TEXT_TO_SPEECH_RANGE_DEFAULTS.rate), ++ ssmlLikePreset: options.ssmlLikePreset || TEXT_TO_SPEECH_DEFAULTS.ssmlLikePreset, ++ text: String(text || "").trim() || "New speech line.", ++ voice: String(options.voice || ""), ++ voiceAge: options.voiceAge || TEXT_TO_SPEECH_DEFAULTS.voiceAge, ++ volume: boundedNumber(options.volume, TEXT_TO_SPEECH_RANGE_DEFAULTS.volume) ++ }; + } + + class TextToSpeechEngine { +@@ -58,6 +233,18 @@ class TextToSpeechEngine { + })); + } + ++ filterVoiceOptions(options = {}) { ++ return filterTextToSpeechVoiceOptions(this.voiceOptions(), options); ++ } ++ ++ shapeOptions(options = {}, sliderOverrides = {}) { ++ return shapeTextToSpeechOptions(options, sliderOverrides); ++ } ++ ++ createQueueItem(options = {}) { ++ return createTextToSpeechQueueItem(options); ++ } ++ + voiceForValue(value) { + const normalizedValue = String(value || "").trim(); + if (!normalizedValue) { +@@ -248,5 +435,18 @@ class TextToSpeechEngine { + } + } + +-export { TextToSpeechEngine }; ++export { ++ ageFilterLabel as textToSpeechAgeFilterLabel, ++ createTextToSpeechQueueItem, ++ filterTextToSpeechVoiceOptions, ++ genderFilterLabel as textToSpeechGenderFilterLabel, ++ payloadGenderValue as textToSpeechPayloadGenderValue, ++ shapeTextToSpeechOptions, ++ textToSpeechLanguageOptionsFromVoices, ++ textToSpeechSlugFromName, ++ textToSpeechVoiceGender, ++ TextToSpeechEngine, ++ uniqueTextToSpeechId, ++ uniqueTextToSpeechName ++}; + export default TextToSpeechEngine; +diff --git a/tests/playwright/tools/TextToSpeechFunctional.spec.mjs b/tests/playwright/tools/TextToSpeechFunctional.spec.mjs +index ccf95e3e3..17e2c0b62 100644 +--- a/tests/playwright/tools/TextToSpeechFunctional.spec.mjs ++++ b/tests/playwright/tools/TextToSpeechFunctional.spec.mjs +@@ -66,7 +66,13 @@ async function openTextToSpeechPage(page, { speechAvailable = true } = {}) { + getVoices() { + return voices; + }, ++ pause() { ++ window.__textToSpeechCalls.push({ type: "pause" }); ++ }, + removeEventListener() {}, ++ resume() { ++ window.__textToSpeechCalls.push({ type: "resume" }); ++ }, + speak(utterance) { + window.__textToSpeechCalls.push({ + lang: utterance.lang, +@@ -101,8 +107,22 @@ test("Text To Speech page loads and speaks through browser speech synthesis", as + await expect(page.locator("[data-tts-voice-select]")).toContainText("Arcade Voice"); + await expect(page.locator("[data-tts-voice-count]")).toHaveText("2"); + await expect(page.locator("[data-tts-engine-label]")).toHaveText("Ready"); ++ await expect(page.locator("[data-tts-gender-select]")).toBeVisible(); ++ await expect(page.locator("[data-tts-language-select]")).toBeVisible(); ++ await expect(page.locator("[data-tts-age-select]")).toBeVisible(); ++ await expect(page.locator("[data-tts-character-preset-select]")).toBeVisible(); ++ await expect(page.locator("[data-tts-ssml-preset-select]")).toBeVisible(); ++ await expect(page.locator("[data-tts-import-json]")).toBeEnabled(); ++ await expect(page.locator("[data-tts-copy-json]")).toBeEnabled(); ++ await expect(page.locator("[data-tts-export-json]")).toBeEnabled(); + + await page.locator("[data-tts-text-input]").fill("Launch the next wave."); ++ await page.locator("[data-tts-item-name]").fill("Wave intro"); ++ await page.locator("[data-tts-character-preset-select]").selectOption("dramatic"); ++ await page.locator("[data-tts-age-select]").selectOption("teen"); ++ await page.locator("[data-tts-ssml-preset-select]").selectOption("whisper-ish"); ++ await expect(page.locator("[data-tts-pitch-value]")).toHaveText("1.1"); ++ await expect(page.locator("[data-tts-volume-value]")).toHaveText("0.6"); + await page.locator("[data-tts-voice-select]").selectOption("arcade-voice-uri"); + await page.locator("[data-tts-rate]").fill("1.4"); + await page.locator("[data-tts-pitch]").fill("0.8"); +@@ -111,6 +131,13 @@ test("Text To Speech page loads and speaks through browser speech synthesis", as + await expect(page.locator("[data-tts-pitch-value]")).toHaveText("0.8"); + await expect(page.locator("[data-tts-volume-value]")).toHaveText("0.55"); + await expect(page.locator("[data-tts-text-count]")).toHaveText("21"); ++ await page.locator("[data-tts-add-item]").click(); ++ await expect(page.locator("[data-tts-queue-list]")).toContainText("Wave intro"); ++ await expect(page.locator("[data-tts-output-summary]")).toContainText("\"name\": \"Wave intro\""); ++ await page.locator("[data-tts-duplicate-item]").click(); ++ await expect(page.locator("[data-tts-queue-list]")).toContainText("Wave intro 2 copy"); ++ await page.locator("[data-tts-delete-item]").click(); ++ await expect(page.locator("[data-tts-queue-list] [data-tts-queue-item]")).toHaveCount(2); + + await expect(page.locator("[data-tts-speak]")).toBeEnabled(); + await page.locator("[data-tts-speak]").click(); +@@ -126,10 +153,13 @@ test("Text To Speech page loads and speaks through browser speech synthesis", as + volume: 0.55, + })); + ++ await page.locator("[data-tts-pause]").click(); ++ await page.locator("[data-tts-resume]").click(); + await page.locator("[data-tts-stop]").click(); + await expect(page.locator("[data-tts-status]")).toContainText("Speech stopped"); + calls = await page.evaluate(() => window.__textToSpeechCalls); + expect(calls.at(-1)).toEqual({ type: "cancel" }); ++ expect(calls).toEqual(expect.arrayContaining([{ type: "pause" }, { type: "resume" }])); + + expect(failures.failedRequests).toEqual([]); + expect(failures.pageErrors).toEqual([]); +diff --git a/toolbox/text-to-speech/index.html b/toolbox/text-to-speech/index.html +index adaaff3a7..f5b29b19a 100644 +--- a/toolbox/text-to-speech/index.html ++++ b/toolbox/text-to-speech/index.html +@@ -6,7 +6,7 @@ + + + Text To Speech - GameFoundryStudio +- ++ + + + +@@ -18,52 +18,100 @@ +
+
Project Workspace / Audio
+

Text To Speech

+-

Preview spoken game text with local browser speech synthesis before deciding whether a generated audio provider is needed.

++

Create named speech lines, shape browser voices, and preview spoken game text through the shared engine audio Text To Speech module.

+
+ +
+
++ ++ +
+ +
+
+-

Browser Preview

+-

Use the browser Web Speech API for immediate local preview. This does not create files, call paid providers, or fake generated audio.

+-
++

Speech Composition

++
+
0Characters
+
0Voices
+
CheckingEngine
+
+
+
+-
+-
Preview Controls
+-

Speak Browser Preview

+-
+-
+- ++ ++ ++
++ ++ ++ + +
+
Loading browser Text To Speech.
+@@ -72,45 +120,30 @@ +
+
+
+-
Ownership Boundary
+-

Preview Only

++
Named Sentences
++

Queue

+
+-

Message text remains Design-owned. Browser preview is local playback only. Future generated audio files remain Audio-owned when a real provider/export flow is added.

++
+
+
+
+
+ +
-

Browser Preview

-

Use the browser Web Speech API for immediate local preview. This does not create files, call paid providers, or fake generated audio.

-
+

Speech Composition

+
0Characters
0Voices
CheckingEngine
-
-
Preview Controls
-

Speak Browser Preview

-
-
- + + +
+ + +
Loading browser Text To Speech.
@@ -72,45 +120,30 @@

Speak Browser Preview

-
Ownership Boundary
-

Preview Only

+
Named Sentences
+

Queue

-

Message text remains Design-owned. Browser preview is local playback only. Future generated audio files remain Audio-owned when a real provider/export flow is added.

+