- Reposition download button from output area to button container
- Place next to Copy and Run buttons with consistent styling
- Button hidden by default, shown only when artifact available
- Remove colored background to match other action buttons
Co-authored-by: Claude <noreply@anthropic.com>
* Enable binary downloads on timeout/cancellation
When code execution times out or is cancelled, the compiled binary
may still be available. This change ensures displayExecutionResults()
is called for timeout/cancelled jobs, allowing users to download
the binary artifact even when execution doesn't complete normally.
* Fix partial output display for timeout/cancellation
Previous commit broke partial output display by passing job.result
directly to displayExecutionResults(), but for timeout/cancelled jobs
the output is in partial_output field, not stdout.
Now properly maps partial_output to stdout before displaying, so users
see both the error message and any output that was captured before
timeout/cancellation, plus binary downloads if available.
* Add debugging for missing artifact on timeout/cancel
Check multiple possible locations for artifact:
- job.artifact (top level)
- job.result.artifact (nested)
Add console logging to see full job structure when timeout/cancel
occurs so we can understand why the binary isn't appearing.
* Try fetching artifact from separate endpoint on timeout/cancel
When timeout/cancel occurs, the artifact isn't in the job response.
Try fetching from /jobs/{job_id}/artifact endpoint as a fallback.
This explores whether the executor service has a separate artifact
endpoint that we can use to retrieve compiled binaries even when
execution is cancelled or times out.
* Remove debug logging, document artifact limitation
Removed console.log debugging statements now that we've confirmed
the executor service doesn't include artifacts in timeout/cancelled
responses and doesn't have a /jobs/{job_id}/artifact endpoint.
Kept the artifact fetching code with comments for future compatibility
if the executor service adds this feature.
Current limitation: Binary downloads only work for completed executions,
not for timeout/cancelled ones. The binary exists but the executor
service doesn't return it.
* Try multiple artifact endpoint patterns for timeout/cancel
When artifact isn't in the job response, try fetching from:
- /artifacts/{job_id}
- /jobs/{job_id}/artifact
- /jobs/{job_id}/download
- /jobs/{job_id}/binary
- /download/{job_id}
- /binary/{job_id}
Handles both JSON responses and direct binary responses. Logs
each attempt to console so we can see which endpoint (if any) works.
* Revert endpoint searching - artifact should be in /jobs/{id}
According to OpenAPI spec, there are no separate artifact endpoints.
The artifact should be included in GET /jobs/{id} response for ALL
job statuses (completed, cancelled, timeout).
Current limitation: The executor service only includes result.artifact
for "completed" status, not for "cancelled" or "timeout" status.
The frontend code is correct - it checks job.artifact and
job.result.artifact. The issue is the executor service needs to
include the artifact in cancelled/timeout responses.
* Add debug logging for cancelled/timeout artifact checks
Since the executor service was supposedly patched to include artifacts
in GET /jobs/{id} responses even for cancelled/timeout jobs, add
detailed logging to verify:
1. What the full job response looks like
2. Whether artifact is at job.artifact or job.result.artifact
3. Artifact details if found
This will help determine if the patch is deployed and working.
* Add test-artifact Makefile target for testing executor API
Tests binary artifact retrieval from code executor service:
- Compiles C code with return_artifact=true
- Extracts base64 artifact from response
- Decodes and executes the binary
Can test against different URLs:
make test-artifact URL=https://code.ai.unturf.com
Tested against production and confirmed:
- Artifacts ARE included for completed jobs
- Artifacts are NOT included for cancelled/timeout jobs (even with
return_artifact=true). Exit code 137 indicates SIGKILL.
* Document confirmed limitation - no artifacts for cancelled jobs
Tested against production executor API (make test-artifact) and confirmed:
- Cancelled jobs return exit_code 137 (SIGKILL)
- NO artifact field in response (neither job.artifact nor job.result.artifact)
- Artifacts only returned for fully completed jobs
Code still checks for artifacts in case this limitation is fixed
in the future, but currently binary downloads will not work for
cancelled/timeout executions.
To fix: Executor service needs to include compiled binary in
response even when execution is killed (compilation succeeded).
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Request compiled binaries via return_artifact parameter
- Add "Download Binary" button when artifact is available
- Support base64 decoding and browser download
- Handle artifact errors gracefully
- Works with C, C++, Rust, Go, Java, and other compiled languages
Co-authored-by: Claude <noreply@anthropic.com>
Problem:
- Buttons were being inserted as children of <pre> element
- This caused buttons to appear inside code blocks with wrong styling
- Template caching made changes appear to require "two commits"
Solution:
- Change insertion point from block.parentNode to preElement.parentNode
- This places buttons as siblings of <pre>, not children
- Add TEMPLATES_AUTO_RELOAD=True to prevent Flask template caching
Technical Details:
- block is the <code> element
- block.parentNode is the <pre> element
- preElement.parentNode.insertBefore puts buttons after <pre>
- Previous code put buttons inside <pre> after <code>
DOM Structure Before:
<pre>
<code>...</code>
<buttons> <!-- Wrong: inside pre -->
</pre>
DOM Structure After:
<pre>
<code>...</code>
</pre>
<buttons> <!-- Correct: after pre -->
Co-authored-by: Claude <noreply@anthropic.com>
* Move code block buttons below code instead of above
- Modified addCopyButtonToCodeBlock to insert button container after code block
- Updated truncateCodeBlock to remove duplicate buttons before adding its own
- Ensures clean button placement for both regular and truncated code blocks
* Refactor code block button rendering for efficiency
- Process blocks in optimal order: truncate → highlight → line numbers → buttons
- Eliminate redundant button creation/removal cycle
- truncateCodeBlock now only truncates and returns boolean
- addCopyButtonToCodeBlock handles all button creation (including Show More)
- Buttons always appear below code blocks after full processing
This prevents wasteful creation and immediate deletion of buttons for truncated blocks.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Add download button for TTS audio
- Add download button next to Play button for all messages
- Button is initially hidden and appears after TTS audio is generated
- Works for both manual play and auto-play modes
- Works for both regular and streaming messages
- Handles cached audio properly
- Download filename includes message ID and voice name
* Refactor: Extract download button logic into helper function
- Create enableDownloadButton() helper to eliminate code duplication
- Replace 4 identical blocks (56 lines) with 4 function calls (4 lines)
- Improves maintainability and follows DRY principle
- Handles both cached and fresh audio in both speakText functions
* Remove hardcoded voice fallbacks, use API or empty list
- Remove hardcoded voice options from HTML dropdown
- Remove all fallbacks to default voices (tts-1:onyx)
- If voices API fails, leave dropdown empty instead of falling back
- localStorage persistence for voice selection already implemented
- Voices API caching already working (1-minute cache like models)
- Voice selection now purely driven by API response
* Fix: Make download button visible after TTS audio loads
- Add download button to previous_messages handler (was missing)
- Change display from "" to "inline-block" for visibility
- Download button now appears properly after TTS processes
* Add debug logging for download button issue
- Add console.log to trace enableDownloadButton execution
- Change === to == for messageId comparison (handle type coercion)
- Log wrapper status, button status, and ID matching
- This will help identify why download button doesn't appear
* Remove debug logging, keep type coercion fix
- Remove console.log statements now that issue is identified
- Keep == comparison (was the actual fix)
- Add comment explaining why == instead of ===
- dataset.messageId is string, messageId param is number
---------
Co-authored-by: Claude <noreply@anthropic.com>
The integration tests were failing in GitHub Actions with 'unable to open
database file' errors because the Flask instance directory didn't exist.
The app.py code at line 40 creates a database URI using app.instance_path,
which requires that directory to exist. In GitHub Actions, this directory
doesn't exist by default, causing SQLite to fail when trying to create
the database file (even though tests override to use :memory:).
Solution: Create instance directory in setUp() before app context is pushed.
Changes:
- Add os.makedirs(app.app.instance_path, exist_ok=True) in setUp()
- Also fixed temp file paths to use absolute paths for research directory
- All 9 integration tests now pass locally
This ensures tests work in both local and GitHub Actions environments.
Fixed the integration tests to use absolute paths when creating
temporary activity YAML files in the research directory.
The tests were failing in GitHub Actions with "unable to open database
file" errors because they used relative paths (Path("research")) which
didn't work correctly in the GitHub Actions working directory.
Changes:
- Use Path(__file__).parent.parent.parent to get absolute base directory
- Apply absolute path to both file creation and cleanup operations
- All 9 integration tests pass locally
This ensures tests work consistently across local development and
GitHub Actions environments.
Fixed the functional test failure by removing MODEL_ENDPOINT_0 from
the functional test step in GitHub Actions workflow.
The functional test test_initialize_model_map_with_env_vars sets its
own test endpoints (MODEL_ENDPOINT_1, MODEL_ENDPOINT_2) and was failing
because MODEL_ENDPOINT_0 from the workflow was interfering.
Changes:
- .github/workflows/test.yml: Removed MODEL_ENDPOINT_0 from functional test step
- .github/workflows/test.yml: Updated unit/integration tests to use hermes.ai.unturf.com
- tests/functional/test_guarded_ai.py: Fixed patch.dict to use clear=False
Unit and integration tests still have MODEL_ENDPOINT_0 configured
since they need it for app initialization. Functional tests now run
without env var interference and can test their own endpoint configs.
All 46 functional tests pass locally.
Fixed remaining 2 integration test failures:
1. Socketio mocking issue:
- Tests were setting app.socketio but activity module has its own reference
- Fixed by mocking activity.socketio directly instead of app.socketio
- Updated test_cancel_activity_integration to check both chat_message and activity_status events
- Updated test_display_activity_metadata_integration to use activity.socketio
2. GitHub Actions environment variables:
- Added MODEL_ENDPOINT_0 and MODEL_API_KEY_0 to all test steps
- These are required for app.py initialization
- Set to dummy values (https://test.api) for testing
Test Results:
- Before: 2 failed, 39 passed
- After: 41 passed ✅
All integration tests now pass locally and should pass on GitHub Actions.
Fixed 3 critical issues:
1. SQLAlchemy 'already registered' error in test_app_activity_functions.py:
- Removed access to db.engine before app context was pushed (line 39)
- Moved db.engine.dispose() to after context.push() (line 54)
- Removed unnecessary init_activity_module() call in tests
- Fixes 9 'Working outside of application context' errors
2. Attempts counter not incrementing for incorrect answers:
- Added 'incorrect' to list of categories that stay on current step
- Previously 'incorrect' was entering navigation block incorrectly
- Now properly goes to ELSE block which increments attempts
- Fixed in activity.py line 1082
Test Results:
- Before: 10 failed, 31 passed
- After: 2 failed, 39 passed
- Remaining 2 failures are minor socketio mocking issues (unrelated)
- Core functionality tests (attempts increment, correct navigation) now pass
Root Cause:
The activity.py logic assumed any category NOT in the special list should
try to navigate forward. But 'incorrect' should stay on the current step
and increment attempts, not try to find the next step.
- Remove db.init_app() call causing 'already registered' error
- Use db.engine.dispose() to clear existing engine
- Use db.session.remove() to clean up sessions
- Forces new connection with in-memory database config
- Fixes 10 failing tests in test_app_activity_functions.py
- Fix test_app_activity_functions.py SQLAlchemy database issues:
- Reinitialize db with test app config before creating tables
- Store and restore original database URI in tearDown
- Add try/except around drop_all in tearDown
- Fix test_activity_integration.py attempts increment test:
- Remove next_section_and_step from incorrect transition
- When next_section_and_step is specified, code navigates without incrementing attempts
- Transition should only have counts_as_attempt without navigation to increment and stay on same step
- This matches the actual behavior: navigation happens immediately when specified
- Fix test_activity_processing.py: Import activity module and use activity.* functions
- Fix test_app_activity_functions.py: Import activity module, use activity.* functions, initialize activity module with app's socketio and db
- Fix test_activity_integration.py: Update YAML format to match current specification
- Change buckets from objects to simple string lists
- Use next_section_and_step instead of separate next_section_id/next_step_id
- Add required title fields and tokens_for_ai
- Replace type field with content_blocks for info steps
- Remove matrix testing against Python 3.10, 3.11, 3.12
- Use Python 3.13 exclusively in both test and lint jobs
- Matches local development environment (Python 3.13.7)
- Ensures consistent behavior between local and CI environments
Fixed 3 failing tests by correcting mock setup:
1. test_bedrock_streaming_protocol: Changed from mocking app.get_s3_client
to mocking boto3.client directly, since chat_claude creates its own client
2. test_streaming_content_accumulation: Fixed Message mock patching and
changed query mock to return mock_message instead of None
3. test_error_handling_in_streaming: Fixed Message mock patching, changed
query mock to return mock_message, and updated assertion to check for
chat_message event instead of message_chunk with is_complete flag
All streaming protocol tests now pass.
The exec() function was using empty globals dict which prevented list
comprehensions from accessing variables in the local scope. Changed to
use the same dict for both globals and locals to properly support
comprehensions in processing scripts.
Fixes battleship game flow tests that use list comprehensions.
- Update test_initialize_model_map to mock models.list() response properly
- Update test_get_openai_client_and_model_default to match new MODEL_X behavior
- Fix test_initialize_model_map_with_env_vars in functional tests
Tests now properly mock the OpenAI client's models.list() response, which
returns model IDs that are used as keys in MODEL_CLIENT_MAP, not endpoint names.
Enhance the activity YAML validator to detect and reject Jinja2 and
Handlebars control structures, enforcing the substitution-only template
system design.
Changes:
- Add regex patterns for Jinja2 ({% %}) and Handlebars ({{# }})
- Add _check_template_syntax() method
- Integrate checks in content_blocks, questions, tokens_for_ai, hints
- Add 7 comprehensive unit tests for template validation
- All 59 activity YAMLs + SPEC.yaml pass validation (0 errors)
Update the attempt counter example in SPEC.yaml to use substitution-only
template syntax instead of Jinja2 control structures ({% if %}).
The AI can naturally understand attempt context from {{current_attempt}},
{{max_attempts}}, and {{attempts_remaining}} variables without needing
conditional logic in the template itself.
This aligns with the substitution-only template system where logic lives
in scripts and templates only display pre-computed values.
- Run unit, functional, and integration tests on push/PR
- Test on Python 3.11 with Ubuntu latest
- Include code coverage reporting for unit tests
- Add linting job with black and flake8
- Validate all activity YAML files
- Trigger on main, master, develop, and claude/** branches
Removed "attempts > 0" check in research/guarded_ai.py that prevented
hints from showing on the first attempt. Matches the fix made to
activity.py for consistent behavior across web app and CLI simulator.
Removed "activity_state.attempts > 0" check that prevented hints from
showing on the first attempt. The code already correctly computes
current_attempt as activity_state.attempts + 1, so hints now work
starting from attempt 1 (when attempts = 0).
- Created 58 unit tests covering all 8 utility functions
- Tests cover template rendering, metadata conditions, conditional content,
navigation, weighted random, progressive hints, and context creation
- Fixed operator precedence bug: _not_contains and _not_exists must be
checked before _contains and _exists to prevent false matches
- All tests passing (58/58)
SPEC.yaml fixes:
- Comment out orphaned example code blocks that broke YAML parsing
- Convert progressive hints and dynamic question examples to comments
- Add placeholder keys to maintain valid YAML structure
- All examples now documented but non-executable (reference only)
- Validates with 0 errors
guarded_ai.py refactor (CLI simulator now uses v2.0 features):
- Import activity_utils.py for consistency with activity.py
- Use check_conditions() for advanced metadata conditions (gte, lt, contains, etc.)
- Use filter_content_blocks() for template rendering and conditional blocks
- Use render_template() for dynamic question text with {{variables}}
- Use resolve_conditional_navigation() for if/elif/else navigation
- Use select_weighted_random() for weighted random selection
- Use get_progressive_hint() for progressive hints system
- Create template contexts with built-in variables (current_attempt, etc.)
Benefits:
- Single source of truth for v2.0 logic (activity_utils.py)
- CLI simulator now tests all v2.0 features
- Maintainability: changes to features only need updates in one place
- Consistency: web app and CLI behave identically
All changes validated and tested.
Add comprehensive v2.0 features to enhance activity creation:
Features Implemented:
- Template variables: {{metadata.key}}, {{current_attempt}}, etc.
- Conditional content blocks: show_if conditions for dynamic content
- Advanced metadata conditions: gte, lt, contains, regex, exists operators
- Conditional navigation: if/elif/else branching based on metadata
- Progressive hints system: Auto-display hints based on attempt number
- Weighted random selection: Probabilistic outcomes with custom weights
- Dynamic question text: Questions with template variables
- Built-in attempt counters: Access to current_attempt, max_attempts, attempts_remaining
Files Modified:
- activity.py: Integrated all v2.0 features into activity execution
- activity_utils.py: New utility module for templates and conditions
- activity_yaml_validator.py: Updated validator for v2.0 schema
- CLAUDE.md: Added session persistence and Twitch Plays model docs
- research/SPEC.yaml: Comprehensive v2.0 feature documentation
Added:
- research/activity-test-v2-features.yaml: Test activity demonstrating all features
All changes validated and tested. Zero errors in validator.
Added mandatory validation step to activity creation workflow:
1. Read research/SPEC.yaml first (fresh spec)
2. Validate with activity_yaml_validator.py after changes
3. All YAMLs must pass validation (0 errors) before committing
Ensures quality and prevents broken activity files from entering the repo.
- Add random bucket names (emergency, surprise, bonus) to main buckets list
- Fix invalid transition targets to use existing steps
- Add tokens_for_ai to all feedback_prompts (required field)
- Add bonus transition definition
All validation errors resolved - SPEC.yaml now passes validation
Random Bucket System:
- Probabilistic events that trigger alongside user responses
- Random rolls before categorization to prevent AI bias
- Multiple random events can trigger simultaneously
- User bucket processed first, random events layer on top
- Metadata accumulates across all transitions
- Last transition's navigation wins
Implementation:
- activity.py: Core random bucket rolling logic
- activity_yaml_validator.py: Validation for random_buckets config
- research/guarded_ai.py: CLI simulator with random event display
- tests/unit/test_random_buckets.py: 22 comprehensive tests (all passing)
Fashion Empire Enhancement:
- activity40-fashion-empire-backrooms.yaml: Added random events to 4 zones
- fashion_emergency (5%): Urgent crises testing leadership
- creative_opportunity (10%): Breakthroughs rewarding innovation
- surprise_client (5%): VIP visitors recognizing reputation
- Random events enhance gameplay without hijacking user intent
Documentation:
- research/SPEC.yaml: Complete YAML specification with verbose comments
- All metadata operations (string concat, numeric ops, random)
- Random buckets with flow explanation
- Feedback prompts (multi-agent system)
- Processing scripts (pre_script, processing_script)
- Model overrides (classifier_model, feedback_model)
- Termination patterns and best practices
- Validation rules and examples
New Activities:
- activity-nuclear-power-plant-ai.yaml: Nuclear reactor control simulation
- activity-submarine-simulation.yaml: Deep sea exploration
- activity-unwaste-factory.yaml: Recycling facility management
Testing:
✅ All 22 random bucket tests passing
✅ YAML validation passing for all activities
✅ Deterministic triple-trigger test (100% probability)
Major expansion to support travel to ANY location during biblical timeline:
NEW REGIONS SUPPORTED:
- Biblical Lands: All biblical eras from Garden of Eden to persecution
- Greece: Philosophers (Socrates, Plato, Aristotle), mystery religions, gods
- Rome: Stoics, emperors, gladiators, early Christians, Roman religion
- India: Buddhist monks, Hindu gurus, yogis, karma/reincarnation
- China: Confucius, Laozi, Taoism, Confucianism, ancestor worship
- Persia: Zoroastrian magi, fire temples, dualism
- Other: Arabia, Africa, Britain, Celtic druids, etc.
KEY FEATURES:
- Geography-aware classifier: Detects both TIME and PLACE from user input
- Dynamic briefings: AI generates context for any location/time combination
- Examples: "30 AD Greece" → Athens philosophers, "500 BC India" → Buddhist monks
- NPC system supports non-biblical spiritual figures
- Conversation system respects all spiritual traditions
- Maintains Temple accuracy for biblical lands
EXAMPLES NOW WORK:
- "Take me to 30 AD Greece" → Meet Stoic philosophers
- "500 BC India" → Meet Buddha's followers
- "Moses" → Egypt ~1446 BC
- "Socrates" → Athens ~400 BC
- "Confucius" → China ~500 BC
- "Garden of Eden" → Paradise before Fall ~4000 BC
File: 662 lines (was 532), validates with 0 errors
Major changes:
- Reduced from 1608 to 532 lines (70% reduction)
- Single open-ended question: "Where/who/when would you like to visit?"
- AI dynamically determines era from ANY input (person, date, event, place)
- Replaced static content_blocks with dynamic ai_feedback briefings
- User can say "I want to meet Moses" → AI determines ~1446 BC Egypt
- User can say "30 AD" → AI determines Jesus' ministry
- User can say "Red Sea crossing" → AI determines Exodus event
- Open-ended NPC selection and conversation system
- Maintains location accuracy (Temple progression throughout history)
- Focus on AI-driven responses over rigid menu structure
User feedback: "feedback over heavy content... needs to be open ended"
Each time travel destination now includes comprehensive briefing:
**Briefing Format:**
- Destination (geographic location)
- Time Period (specific dates)
- Biblical Reference (relevant scripture)
- What You'll Experience (historical context, key events, atmosphere)
- Important/Critical Location Notes (especially Temple status)
**Educational Enhancements:**
Garden of Eden:
- Explains it's before sin, perfect creation
- Notes no buildings/cities exist yet
Fall & Early World:
- Describes life after sin entered
- Notes Cain/Abel, first altars, long lifespans
Egypt & Exodus:
- **CRITICAL:** Emphasizes NO Temple for 500+ more years
- Explains Moses uses simple altars
- Egyptian temples to Ra/Osiris present
Solomon's Temple:
- **HISTORIC MOMENT:** FIRST Temple after 480 years!
- Describes gold overlay, Ark location
- This is what Moses and David longed for
Jesus' Ministry:
- SECOND Temple (Herod's) stands
- Jesus prophesies its destruction
- Will be gone in 40 years (70 AD)
Roman Persecution:
- NO Temple (destroyed 70 AD)
- Christians meet in catacombs
- Fish symbol as secret sign
Makes Temple progression crystal clear: none → altars → First Temple → Second Temple → destroyed → underground faith.
Users now understand WHEN and WHERE they're going before arrival.
COMPLETE REWRITE with all requested features:
**Starts at the Beginning:**
- Garden of Eden (Paradise before sin)
- Fall & Early World (Cain, Abel, Enoch)
**Covers Full Bible Chronologically:**
- Egypt & Exodus (~1446 BC)
- Solomon's Temple (~970 BC)
- Life of Jesus (~30 AD)
- Persecution & Martyrdom (~64-313 AD)
**Open-Ended NPC Selection:**
- Users can request ANY biblical figure from each era
- AI dynamically rolepl ays any character accurately
- Suggestions provided but not limiting
- Examples: "Moses", "Queen of Sheba", "a Hebrew slave"
**Historically Accurate Locations:**
- Garden of Eden: NO buildings, only perfect nature
- Egypt: NO Temple to YHWH (only altars, won't exist for 500+ years)
- Solomon: FIRST Temple in all its glory
- Jesus' time: SECOND Temple (Herod's Temple)
- Persecution: NO Temple (destroyed 70 AD), catacombs instead
**Features:**
- 1608 lines, 9 sections, 25 steps
- Metadata tracking (epochs visited, people met)
- Multilingual support
- Looping time machine hub
- Biblically accurate character portrayals
- Scripture references throughout
Demonstrates location accuracy progression: no temple → Tabernacle/altars → First Temple → Second Temple → no temple (destroyed) → faith survives underground.
Create immersive time travel experience through key Biblical epochs:
- Egypt & Exodus: Meet Moses, Pharaoh, Hebrew slaves, Aaron
- Kingdom of David: Visit King David, Prophet Nathan, musicians, citizens
- Life of Jesus: Walk with Jesus, disciples, Mary Magdalene, crowds
- Pentecost & Early Church: Experience Holy Spirit, meet apostles and converts
- Roman Persecution: Stand with martyrs, Paul, persecuted believers
Features:
- Time machine hub for epoch selection
- Multiple NPCs per epoch with unique personalities
- Biblically accurate dialogue and references
- Metadata tracking for journey statistics
- Looping mechanism to revisit epochs
- Final reflection on spiritual journey
The activity maintains historical accuracy while being engaging and educational.
Create activity40-fashion-empire-backrooms.yaml with:
Features:
- Player is a girl running her own fashion brand underground
- Backrooms aesthetic: liminal warehouse spaces, mysterious locations
- 4 explorable locations: Warehouse Level -3, The Salon, Sub Bay (underwater lab), Reactor Atelier (nuclear power)
- Full control over 70+ robots and NPC employees (Zara-7, Viktor, Mx. Kai, Luna & Sol)
- Mission-based gameplay (15% tasks, 5% emergencies)
- Player makes creative, leadership, and strategic decisions
Locations:
- Warehouse Level -3: Storage backrooms, assembly drones, fabric management
- The Salon: Creative hub, style bots, runway preparation
- Sub Bay: Underwater dye laboratory, bioluminescent experiments, submersibles
- Reactor Atelier: Nuclear-powered textile synthesis, atomic fabric manipulation
Gameplay:
- Choose locations via central elevator
- Complete missions (color selection, robot commands, textile treatments, power management)
- Handle emergencies (fabric contamination crisis with multiple solutions)
- Manage NPCs and give directives
- Make creative vision decisions for runway shows
- Culminates in Neon Dreams runway show featuring player's choices
- Player sees their vision realized through their empire
All transitions validated, proper termination, educational about fashion + leadership
Remove obsolete VALID_VOICES validation that was rejecting the new
"model:voice" format from the voices endpoint integration. The old
validation expected simple voice names like "onyx" but the new format
uses "tts-1:onyx", causing validation to fail and produce malformed
API requests that returned HTTP 400 errors.
Changes:
- Remove VALID_VOICES constant (no longer needed)
- Update syncInputsAndQueryString() to accept any voice value from dropdown
- Default to "tts-1:onyx" format if no value present
Fixes the TTS errors seen in production where voice selection was
failing with HTTP 400 status.