diff --git a/planning_notes/rails-engine-migration-json/review1/00_EXECUTIVE_SUMMARY.md b/planning_notes/rails-engine-migration-json/review1/00_EXECUTIVE_SUMMARY.md new file mode 100644 index 0000000..12fd4d3 --- /dev/null +++ b/planning_notes/rails-engine-migration-json/review1/00_EXECUTIVE_SUMMARY.md @@ -0,0 +1,110 @@ +# Rails Engine Migration Plan - Review 1 +## Executive Summary + +**Date:** November 20, 2025 +**Reviewer:** Claude (Automated Code Review) +**Documents Reviewed:** All files in `planning_notes/rails-engine-migration-json/` +**Codebase State:** Current BreakEscape repository as of commit `e9c73aa` + +--- + +## Overall Assessment + +**Status: MOSTLY SOLID - REQUIRES CRITICAL CORRECTIONS BEFORE IMPLEMENTATION** + +The Rails Engine migration plan is **well-structured** and follows sound architectural principles. The JSON-centric approach is appropriate and will significantly simplify the migration compared to the original complex relational database approach. + +However, **critical discrepancies** have been identified between the plan's assumptions and the actual codebase structure that **MUST** be addressed before beginning implementation. + +--- + +## Key Findings Summary + +### āœ… Strengths + +1. **Simplified Architecture**: JSON-centric JSONB approach is excellent for this use case +2. **Clear Documentation**: 8 comprehensive documents with step-by-step instructions +3. **Hacktivity Compatibility**: Thoroughly validated against actual Hacktivity codebase +4. **Phased Approach**: 12 phases with clear milestones and testing points +5. **Minimal Client Changes**: Plan correctly minimizes client-side rewrites + +### āš ļø Critical Issues Requiring Immediate Attention + +1. **NPC Ink File Structure Mismatch** (CRITICAL) + - Plan assumes: `.ink` and `.ink.json` files for all NPCs + - Reality: 30 `.ink` files, only 3 `.ink.json` files + - Scenarios reference `.json` files (not `.ink.json`) + - **Impact**: Phase 3 file reorganization will fail + +2. **Scenario-NPC Relationship** (HIGH) + - Plan assumes: Each scenario has dedicated NPC scripts + - Reality: Many NPCs are shared across scenarios (test-npc.json used 10+ times) + - **Impact**: Database schema needs adjustment, seed script will fail + +3. **Missing Compilation Step** (CRITICAL) + - Plan doesn't account for compiling `.ink` → `.json` + - No tooling documented for Ink compilation in Rails context + - **Impact**: NPC scripts won't work without compilation pipeline + +4. **Global State Management** (MEDIUM) + - `window.gameState` has expanded beyond what's tracked in plan + - Includes: `biometricSamples`, `bluetoothDevices`, `notes`, `startTime` + - **Impact**: Incomplete state persistence, potential data loss + +5. **Room Loading Architecture** (MEDIUM) + - Plan assumes Tiled JSON maps loaded via API + - Current codebase: Tiled maps loaded statically from `assets/rooms/` + - **Impact**: API endpoint design incomplete + +### šŸ“Š Risk Level: MEDIUM-HIGH + +Without addressing critical issues, implementation will encounter: +- **Build failures** in Phase 3 (file reorganization) +- **Seed failures** in Phase 5 (scenario import) +- **Runtime errors** when NPCs are encountered +- **Incomplete game state** persistence + +--- + +## Recommendation + +**DO NOT BEGIN IMPLEMENTATION** until critical issues are resolved. + +**Recommended Actions:** +1. Update Phase 3 to handle `.ink` → `.json` compilation +2. Add shared NPC script support to database schema +3. Document Ink compilation toolchain +4. Extend `player_state` JSONB schema to include missing gameState fields +5. Clarify room asset loading strategy + +**Estimated Delay:** 1-2 days to update plans, no impact to overall timeline if done first. + +--- + +## Document Structure + +This review is organized into the following sections: + +- **00_EXECUTIVE_SUMMARY.md** (this file) - Overview and key findings +- **01_CRITICAL_ISSUES.md** - Detailed analysis of blocking problems +- **02_ARCHITECTURE_REVIEW.md** - Technical design validation +- **03_MIGRATION_PLAN_REVIEW.md** - Phase-by-phase analysis +- **04_RISKS_AND_MITIGATIONS.md** - Comprehensive risk assessment +- **05_RECOMMENDATIONS.md** - Prioritized improvements and fixes +- **06_UPDATED_SCHEMA.md** - Proposed database schema corrections +- **07_UPDATED_PHASE3.md** - Corrected scenario reorganization steps + +--- + +## Next Steps + +1. **Review Team**: Read critical issues document (01_CRITICAL_ISSUES.md) +2. **Decision**: Approve recommended schema/plan updates +3. **Update Plans**: Incorporate corrections into official docs +4. **Validation**: Re-review updated plans +5. **Implementation**: Begin Phase 1 with confidence + +--- + +**Review Confidence Level:** HIGH +All findings based on direct codebase analysis and plan document review. diff --git a/planning_notes/rails-engine-migration-json/review1/01_CRITICAL_ISSUES.md b/planning_notes/rails-engine-migration-json/review1/01_CRITICAL_ISSUES.md new file mode 100644 index 0000000..be7cc83 --- /dev/null +++ b/planning_notes/rails-engine-migration-json/review1/01_CRITICAL_ISSUES.md @@ -0,0 +1,648 @@ +# Critical Issues - Detailed Analysis + +This document provides in-depth analysis of critical issues that **MUST** be resolved before implementation. + +--- + +## Issue #1: NPC Ink File Structure Mismatch + +**Severity:** šŸ”“ CRITICAL - Will cause build failures +**Phase Impact:** Phase 3, Phase 5 +**Effort to Fix:** 2-4 hours + +### Problem Description + +The migration plan makes incorrect assumptions about Ink file organization: + +**Plan Assumes:** +``` +app/assets/scenarios/ceo_exfil/ink/ +ā”œā”€ā”€ security_guard.ink āœ“ EXISTS +ā”œā”€ā”€ security_guard.ink.json āœ— DOES NOT EXIST +ā”œā”€ā”€ neye_eve.ink āœ— DOES NOT EXIST +ā”œā”€ā”€ neye_eve.ink.json āœ— DOES NOT EXIST +``` + +**Actual Codebase:** +``` +scenarios/ink/ +ā”œā”€ā”€ security-guard.ink āœ“ EXISTS (hyphenated) +ā”œā”€ā”€ neye-eve.json āœ“ EXISTS (compiled, not .ink.json) +ā”œā”€ā”€ gossip-girl.json āœ“ EXISTS +ā”œā”€ā”€ helper-npc.json āœ“ EXISTS +ā”œā”€ā”€ alice-chat.ink āœ“ EXISTS +ā”œā”€ā”€ alice-chat.ink.json āœ“ EXISTS (only 3 files have .ink.json) +└── ... 30 total .ink files +``` + +**Scenarios Reference:** +```json +{ + "storyPath": "scenarios/ink/neye-eve.json" // Not .ink.json! +} +``` + +### Evidence + +From codebase analysis: +```bash +# Ink source files +$ find scenarios/ink -name "*.ink" | wc -l +30 + +# Compiled ink files (.ink.json format) +$ ls scenarios/ink/*.ink.json +alice-chat.ink.json +mixed-message-example.ink.json +voice-message-example.ink.json + +# Regular JSON files (compiled ink without .ink extension) +$ ls scenarios/ink/*.json | wc -l +26 +``` + +From scenario files: +```json +// scenarios/ceo_exfil.json +{ + "npcs": [ + { + "id": "neye_eve", + "storyPath": "scenarios/ink/neye-eve.json" // ← .json, not .ink.json + } + ] +} +``` + +### Root Cause + +The codebase uses **inconsistent naming conventions**: +- Some files: `name.ink.json` (Ink's default output) +- Most files: `name.json` (manually renamed for cleaner paths) +- Scenarios expect: `.json` extension + +Additionally, **not all .ink files have been compiled**: +- 30 source `.ink` files exist +- Only 3-5 compiled files exist +- Missing compilation step in workflow + +### Impact on Migration Plan + +**Phase 3: Reorganize Scenarios (Week 1-2)** +```bash +# This command will FAIL: +mv "scenarios/ink/security_guard.ink.json" "app/assets/scenarios/${SCENARIO}/ink/" +# Error: No such file or directory +``` + +**Phase 5: Scenario Import (Week 2)** +```ruby +# This code will FAIL: +ink_json_path = Rails.root.join('app/assets/scenarios', scenario_name, 'ink', "#{npc_id}.ink.json") +# Error: File not found (looking for .ink.json, but files are .json) +``` + +### Required Fixes + +#### Fix 1: Update File Movement Commands (Phase 3) + +**Before:** +```bash +mv "scenarios/ink/security_guard.ink" "app/assets/scenarios/${SCENARIO}/ink/" +mv "scenarios/ink/security_guard.ink.json" "app/assets/scenarios/${SCENARIO}/ink/" +``` + +**After:** +```bash +# Move .ink source +mv "scenarios/ink/security-guard.ink" "app/assets/scenarios/${SCENARIO}/ink/" + +# Move compiled .json (check both patterns) +if [ -f "scenarios/ink/security-guard.ink.json" ]; then + mv "scenarios/ink/security-guard.ink.json" "app/assets/scenarios/${SCENARIO}/ink/" +elif [ -f "scenarios/ink/security-guard.json" ]; then + mv "scenarios/ink/security-guard.json" "app/assets/scenarios/${SCENARIO}/ink/" +fi +``` + +#### Fix 2: Add Ink Compilation Step + +**New Phase 2.5: Compile Ink Scripts** +```bash +# Install Inklecate (Ink compiler) +npm install -g inkjs + +# Compile all .ink files to .json +for ink_file in scenarios/ink/*.ink; do + base_name=$(basename "$ink_file" .ink) + + # Skip if already compiled as .json + if [ ! -f "scenarios/ink/${base_name}.json" ] && [ ! -f "scenarios/ink/${base_name}.ink.json" ]; then + echo "Compiling $ink_file..." + inklecate -o "scenarios/ink/${base_name}.json" "$ink_file" + fi +done +``` + +#### Fix 3: Update ScenarioLoader (Phase 5) + +**Before:** +```ruby +ink_json_path = Rails.root.join('app/assets/scenarios', scenario_name, 'ink', "#{npc_id}.ink.json") +``` + +**After:** +```ruby +# Check both .json and .ink.json patterns +json_path = Rails.root.join('app/assets/scenarios', scenario_name, 'ink', "#{npc_id}.json") +ink_json_path = Rails.root.join('app/assets/scenarios', scenario_name, 'ink', "#{npc_id}.ink.json") + +compiled_path = File.exist?(ink_json_path) ? ink_json_path : json_path +next unless File.exist?(compiled_path) + +npc_script.ink_compiled = File.read(compiled_path) +``` + +--- + +## Issue #2: Scenario-NPC Relationship (Shared NPCs) + +**Severity:** 🟠 HIGH - Will cause seed failures and data duplication +**Phase Impact:** Phase 4 (Database), Phase 5 (Seeds) +**Effort to Fix:** 3-5 hours + +### Problem Description + +The database schema assumes **1:1 relationship** between scenarios and NPCs: + +**Current Schema:** +```ruby +create_table :break_escape_npc_scripts do |t| + t.references :scenario, null: false + t.string :npc_id, null: false + t.index [:scenario_id, :npc_id], unique: true # ← Assumes unique per scenario +end +``` + +**Actual Codebase:** +Many NPCs are **shared across scenarios**: + +``` +scenarios/ink/test-npc.json → Used in 10+ test scenarios +scenarios/ink/generic-npc.json → Reusable helper NPC +scenarios/ink/haxolottle_hub.json → Shared across hub scenarios +``` + +From grep analysis: +``` +scenarios/test-npc-face-player.json: Uses test-npc.json (10 times) +scenarios/npc-hub-demo-ghost-protocol.json: Uses netherton_hub.json, chen_hub.json, haxolottle_hub.json +``` + +### Impact + +**Database Constraint Violation:** +- Seed script will try to create same NPC for multiple scenarios +- Unique index will fail on second scenario +- OR: Wasteful duplication (same script stored 10+ times) + +**Example Failure:** +```ruby +# Scenario 1: test-npc-face-player +NpcScript.create!(scenario_id: 1, npc_id: 'test_npc', ink_compiled: '...') # āœ“ Success + +# Scenario 2: test-npc-pathfinding +NpcScript.create!(scenario_id: 2, npc_id: 'test_npc', ink_compiled: '...') # āœ“ Success (duplicate!) + +# Result: Same script stored twice, 2x database usage +``` + +### Required Fixes + +#### Option A: Allow Shared NPCs (Recommended) + +**Update Schema:** +```ruby +create_table :break_escape_npc_scripts do |t| + t.string :npc_id, null: false # Remove scenario_id FK + t.text :ink_source + t.text :ink_compiled, null: false + t.timestamps + + t.index :npc_id, unique: true # Global NPC registry +end + +# New join table for scenario-npc relationships +create_table :break_escape_scenario_npcs do |t| + t.references :scenario, null: false, foreign_key: { to_table: :break_escape_scenarios } + t.references :npc_script, null: false, foreign_key: { to_table: :break_escape_npc_scripts } + t.timestamps + + t.index [:scenario_id, :npc_script_id], unique: true +end +``` + +**Pros:** +- Eliminates duplication +- Matches actual codebase usage +- Easier to update shared NPCs globally + +**Cons:** +- Slightly more complex queries +- Migration plan needs updating + +#### Option B: Namespace NPCs per Scenario + +Keep current schema, but change seed logic: + +```ruby +npc_script_id = "#{scenario_name}_#{npc_id}" # e.g., "ceo_exfil_security_guard" +``` + +**Pros:** +- Minimal schema changes +- Simple implementation + +**Cons:** +- Duplicates shared NPCs across scenarios +- Harder to update global NPCs +- Wastes database space + +### Recommendation + +**Use Option A** - The join table approach is more correct and matches the actual sharing patterns in the codebase. + +--- + +## Issue #3: Missing Ink Compilation Pipeline + +**Severity:** šŸ”“ CRITICAL - NPCs won't function without compiled scripts +**Phase Impact:** Phase 2-5 +**Effort to Fix:** 4-6 hours + +### Problem Description + +The plan doesn't document: +1. How to compile `.ink` files to `.json` +2. Where compilation happens (local dev? CI? deployment?) +3. How to handle ERB + Ink compilation together +4. Version control for compiled files + +### Current State + +**What exists:** +- 30 `.ink` source files +- 3-5 compiled `.json` files +- No documented compilation process +- No Rakefile tasks for compilation + +**What's missing:** +- Ink compiler installation instructions +- Automated compilation workflow +- Integration with Rails asset pipeline +- CI/CD compilation step + +### Impact + +**Without compilation:** +```ruby +# Phase 5 seed will fail: +npc_script.ink_compiled = File.read(ink_json_path) +# Error: File not found (never compiled) +``` + +**Runtime impact:** +```javascript +// Client tries to load NPC script +const script = await fetch('/api/games/123/npcs/security_guard/script'); +// Returns empty/null - game breaks +``` + +### Required Fixes + +#### Fix 1: Add Compilation Documentation + +**New Section in 02_IMPLEMENTATION_PLAN.md:** + +```markdown +## Phase 2.5: Compile Ink Scripts (Week 1) + +### Prerequisites + +Install Ink compiler: +```bash +# Option A: Via npm +npm install -g inkjs +npm install -g inklecate-cli + +# Option B: Download binary +wget https://github.com/inkle/ink/releases/download/v1.1.1/inklecate_linux.zip +unzip inklecate_linux.zip +chmod +x inklecate +sudo mv inklecate /usr/local/bin/ +``` + +### Compile All Scripts + +```bash +# Create compilation script +cat > scripts/compile_ink.sh << 'EOF' +#!/bin/bash +for ink_file in scenarios/ink/*.ink; do + base=$(basename "$ink_file" .ink) + json_out="scenarios/ink/${base}.json" + + # Skip if up-to-date + if [ -f "$json_out" ] && [ "$json_out" -nt "$ink_file" ]; then + echo "āœ“ $base.json is up to date" + continue + fi + + echo "Compiling $base.ink..." + inklecate -o "$json_out" "$ink_file" + + if [ $? -eq 0 ]; then + echo " āœ“ Compiled to $base.json" + else + echo " āœ— Compilation failed" + exit 1 + fi +done +EOF + +chmod +x scripts/compile_ink.sh +./scripts/compile_ink.sh +``` + +**Commit:** +```bash +git add scenarios/ink/*.json scripts/compile_ink.sh +git commit -m "feat: Add Ink compilation script and compile all NPCs" +``` +``` + +#### Fix 2: Add Rake Task + +```ruby +# lib/tasks/ink.rake +namespace :break_escape do + namespace :ink do + desc "Compile all .ink files to .json" + task :compile do + require 'open3' + + ink_dir = Rails.root.join('app/assets/scenarios') + compiled = 0 + failed = 0 + + Dir.glob(ink_dir.join('**/*.ink')).each do |ink_file| + json_file = ink_file.gsub(/\.ink$/, '.json') + + # Skip if up-to-date + next if File.exist?(json_file) && File.mtime(json_file) > File.mtime(ink_file) + + puts "Compiling #{File.basename(ink_file)}..." + stdout, stderr, status = Open3.capture3("inklecate", "-o", json_file, ink_file) + + if status.success? + compiled += 1 + puts " āœ“ Compiled" + else + failed += 1 + puts " āœ— Failed: #{stderr}" + end + end + + puts "\nāœ“ Compiled #{compiled} files" + puts "āœ— Failed #{failed} files" if failed > 0 + end + + desc "Watch .ink files and auto-compile on changes" + task :watch do + require 'listen' + + puts "šŸ‘€ Watching for .ink file changes..." + + listener = Listen.to(Rails.root.join('app/assets/scenarios'), only: /\.ink$/) do |modified, added, removed| + (modified + added).each do |file| + Rake::Task['break_escape:ink:compile'].execute + end + end + + listener.start + sleep + end + end +end +``` + +#### Fix 3: Add to Deployment Pipeline + +**Heroku/Production:** +```ruby +# config/initializers/break_escape.rb (after engine initialization) +if Rails.env.production? && !ENV['SKIP_INK_COMPILE'] + Rails.logger.info "Compiling Ink scripts for production..." + Rake::Task['break_escape:ink:compile'].invoke +end +``` + +**CI/CD (.github/workflows/test.yml):** +```yaml +- name: Compile Ink scripts + run: | + npm install -g inklecate-cli + bundle exec rake break_escape:ink:compile +``` + +--- + +## Issue #4: Incomplete Global State Tracking + +**Severity:** 🟔 MEDIUM - State loss, potential bugs +**Phase Impact:** Phase 4 (Schema), Phase 9 (Client) +**Effort to Fix:** 1-2 hours + +### Problem Description + +The `player_state` JSONB schema doesn't include all fields from `window.gameState`: + +**Plan Schema:** +```json +{ + "currentRoom": "...", + "position": {"x": 0, "y": 0}, + "unlockedRooms": [], + "unlockedObjects": [], + "inventory": [], + "encounteredNPCs": [], + "globalVariables": {} +} +``` + +**Actual window.gameState (js/main.js:46):** +```javascript +window.gameState = { + biometricSamples: [], // ← MISSING + biometricUnlocks: [], // ← MISSING + bluetoothDevices: [], // ← MISSING + notes: [], // ← MISSING + startTime: null // ← MISSING +}; +``` + +### Impact + +**Data Loss:** +- Player collects biometric samples → Lost on page reload +- Player scans Bluetooth devices → Lost on page reload +- Player reads notes → Lost on page reload + +**Broken Minigames:** +- Biometrics minigame relies on `biometricSamples` array +- Bluetooth scanner relies on `bluetoothDevices` array +- Notes system relies on `notes` array + +### Required Fix + +**Update Migration (Phase 4):** +```ruby +t.jsonb :player_state, null: false, default: { + currentRoom: nil, + position: { x: 0, y: 0 }, + unlockedRooms: [], + unlockedObjects: [], + inventory: [], + encounteredNPCs: [], + globalVariables: {}, + + # Add missing fields + biometricSamples: [], # { type: 'fingerprint', data: '...', source: '...' } + biometricUnlocks: [], # ['door_ceo', 'safe_123'] + bluetoothDevices: [], # { name: '...', mac: '...', distance: ... } + notes: [], # { id: '...', title: '...', content: '...' } + startTime: nil # ISO8601 timestamp +} +``` + +**Update GameInstance Model:** +```ruby +def sync_minigame_state!(minigame_data) + player_state['biometricSamples'] = minigame_data['biometricSamples'] if minigame_data['biometricSamples'] + player_state['bluetoothDevices'] = minigame_data['bluetoothDevices'] if minigame_data['bluetoothDevices'] + player_state['notes'] = minigame_data['notes'] if minigame_data['notes'] + save! +end +``` + +--- + +## Issue #5: Room Asset Loading Strategy Unclear + +**Severity:** 🟔 MEDIUM - API design incomplete +**Phase Impact:** Phase 6 (API), Phase 9 (Client) +**Effort to Fix:** 2-3 hours + +### Problem Description + +The plan's API design doesn't clarify how Tiled map JSON files are served. + +**Current Codebase (js/core/game.js:32):** +```javascript +// Phaser loads Tiled maps directly +this.load.tilemapTiledJSON('room_reception', 'assets/rooms/room_reception2.json'); +this.load.tilemapTiledJSON('room_office', 'assets/rooms/room_office2.json'); +``` + +**Plan's API (01_ARCHITECTURE.md:494):** +``` +GET /api/games/:game_id/rooms/:room_id + +Response: +{ + "roomId": "room_office", + "type": "office", + "connections": {...}, + "objects": [...] +} +``` + +**Confusion:** +- Does API return Tiled map JSON or filtered game data? +- Where do Tiled `.json` files live after migration? +- How does Phaser load Tiled maps (static assets vs API)? + +### Current Structure + +``` +assets/rooms/ +ā”œā”€ā”€ room_reception2.json (Tiled map) +ā”œā”€ā”€ room_office2.json (Tiled map) +ā”œā”€ā”€ room_ceo2.json (Tiled map) +... + +scenarios/ceo_exfil.json (Game logic: objects, NPCs, locks) +``` + +**Two different data structures:** +1. **Tiled maps** (.json) - Visual layout, tiles, collision layers +2. **Scenario data** (.json) - Game objects, locks, NPCs, connections + +### Required Clarification + +**Recommended Approach:** + +1. **Keep Tiled maps as static assets:** +```bash +# Move to public/break_escape/ +mv assets/rooms public/break_escape/assets/ +``` + +2. **API returns game logic only:** +```ruby +def show + room_data = @game_instance.scenario.filtered_room_data(params[:room_id]) + + render json: { + roomId: params[:room_id], + + # Game logic (from scenario) + connections: room_data['connections'], + objects: room_data['objects'], + npcs: room_data['npcs'], + locked: room_data['locked'], + + # Tiled map reference (loaded separately by Phaser) + tiledMap: room_data['type'] # e.g., 'room_reception' + } +end +``` + +3. **Client loads both:** +```javascript +// 1. Load Tiled map (static asset) +this.load.tilemapTiledJSON(roomData.tiledMap, `/break_escape/assets/rooms/${roomData.tiledMap}.json`); + +// 2. Load game logic (API) +const gameData = await apiGet(`/rooms/${roomId}`); +``` + +--- + +## Summary of Required Fixes + +| Issue | Severity | Phase | Effort | Priority | +|-------|----------|-------|--------|----------| +| #1: Ink file structure | šŸ”“ Critical | 3, 5 | 2-4h | P0 | +| #2: Shared NPCs | 🟠 High | 4, 5 | 3-5h | P0 | +| #3: Ink compilation | šŸ”“ Critical | 2-5 | 4-6h | P0 | +| #4: Global state | 🟔 Medium | 4, 9 | 1-2h | P1 | +| #5: Room assets | 🟔 Medium | 6, 9 | 2-3h | P1 | + +**Total Effort:** 12-20 hours (1.5-2.5 days) + +**Risk if Not Fixed:** Implementation will fail at Phase 3 and require rework. + +--- + +**Next Document:** [02_ARCHITECTURE_REVIEW.md](./02_ARCHITECTURE_REVIEW.md) - Validation of technical design decisions diff --git a/planning_notes/rails-engine-migration-json/review1/02_ARCHITECTURE_REVIEW.md b/planning_notes/rails-engine-migration-json/review1/02_ARCHITECTURE_REVIEW.md new file mode 100644 index 0000000..ab767aa --- /dev/null +++ b/planning_notes/rails-engine-migration-json/review1/02_ARCHITECTURE_REVIEW.md @@ -0,0 +1,453 @@ +# Architecture Review + +This document validates the technical design decisions in the migration plan. + +--- + +## Database Design + +### āœ… JSON-Centric Approach - EXCELLENT + +**Decision:** Use JSONB columns for flexible state storage instead of normalized relational tables. + +**Validation:** āœ… **CORRECT CHOICE** + +**Reasoning:** +1. **Game state is hierarchical** - Perfect fit for JSON structure +2. **Rapid iteration** - Can add new state fields without migrations +3. **PostgreSQL JSONB performance** - GIN indexes provide fast queries +4. **Scenario data varies** - Each scenario has different objects/rooms +5. **Reduces complexity** - 3 tables vs 10+ with proper indexes + +**Evidence from similar systems:** +- Many game backends use document stores (MongoDB, Firestore) +- Rails + JSONB provides best of both worlds +- Hacktivity already uses PostgreSQL (validated in 05_HACKTIVITY_INTEGRATION.md) + +**Recommendation:** āœ… Keep this approach + +--- + +## Polymorphic Player Association + +### āœ… Polymorphic Design - CORRECT + +**Decision:** Use polymorphic `belongs_to :player` for User or DemoUser. + +```ruby +belongs_to :player, polymorphic: true +``` + +**Validation:** āœ… **CORRECT FOR REQUIREMENTS** + +**Reasoning:** +1. **Supports standalone mode** - DemoUser for development +2. **Integrates with Hacktivity** - Uses existing User model +3. **Rails standard pattern** - Well-documented and tested +4. **Authorization compatible** - Pundit handles polymorphic associations + +**Hacktivity Compatibility:** +```ruby +# Hacktivity User model has methods needed: +user.admin? # āœ“ Exists +user.account_manager? # āœ“ Exists +user.handle # āœ“ Exists +``` + +**Recommendation:** āœ… Keep this approach + +--- + +## API Design + +### āœ… Session-Based Authentication - APPROPRIATE + +**Decision:** Use Rails session-based auth (not JWT). + +**Validation:** āœ… **MATCHES HACKTIVITY ARCHITECTURE** + +**Reasoning:** +1. **Hacktivity uses Devise** - Session-based by default +2. **Simplifies integration** - No token management needed +3. **CSRF protection** - Built-in with Rails +4. **Secure cookies** - HTTPOnly, Secure flags +5. **No mobile app requirement** - Web-only use case + +**Alternative Considered:** JWT tokens +**Why Rejected:** Adds complexity without benefit for web-only game + +**Recommendation:** āœ… Keep session-based auth + +--- + +### āœ… 6 API Endpoints - MINIMAL AND SUFFICIENT + +**Decision:** Limit API surface to 6 essential endpoints. + +**Validation:** āœ… **CORRECT SCOPE** + +**Endpoints:** +1. `GET /bootstrap` - Initial game data +2. `PUT /sync_state` - Periodic state sync +3. `POST /unlock` - Server-side validation +4. `POST /inventory` - Inventory updates +5. `GET /rooms/:id` - Load room data +6. `GET /npcs/:id/script` - Lazy-load NPC scripts + +**Coverage Analysis:** +- āœ… Unlocking (critical validation) +- āœ… Inventory management +- āœ… NPC interactions +- āœ… Room discovery +- āœ… State persistence +- āš ļø Missing: Minigame results, combat events (see recommendations) + +**Recommendation:** āœ… Keep core 6, consider 2 additions (see 05_RECOMMENDATIONS.md) + +--- + +## Database Schema + +### āš ļø NPC Scripts Table - NEEDS ADJUSTMENT + +**Current Design:** +```ruby +create_table :break_escape_npc_scripts do |t| + t.references :scenario, null: false + t.string :npc_id, null: false + t.index [:scenario_id, :npc_id], unique: true +end +``` + +**Issue:** Doesn't support shared NPCs across scenarios. + +**Validation:** āŒ **INCORRECT FOR CODEBASE** + +**Evidence:** +- `test-npc.json` used in 10+ scenarios +- `generic-npc.json` designed for reuse +- Hub NPCs (`haxolottle_hub.json`) shared across scenarios + +**Recommendation:** āš ļø See recommended fix in 06_UPDATED_SCHEMA.md + +--- + +### āœ… Game Instances Table - WELL DESIGNED + +**Schema:** +```ruby +create_table :break_escape_game_instances do |t| + t.references :player, polymorphic: true + t.references :scenario + t.jsonb :player_state, default: { ... } + t.string :status + t.datetime :started_at, :completed_at + t.integer :score, :health +end +``` + +**Validation:** āœ… **SOLID DESIGN** (with minor additions) + +**Strengths:** +1. Polymorphic player āœ… +2. JSONB for flexible state āœ… +3. Status tracking āœ… +4. Timestamps for analytics āœ… +5. GIN index on player_state āœ… + +**Missing Fields (non-critical):** +- `attempts` counter - How many times player retried +- `hints_used` - Track hint system usage +- `time_elapsed` - For leaderboards + +**Recommendation:** āœ… Core design is good, minor additions recommended + +--- + +## Routes Design + +### āœ… Engine Mounting - CORRECT PATTERN + +**Decision:** Mount engine at `/break_escape` in Hacktivity. + +```ruby +# Hacktivity config/routes.rb +mount BreakEscape::Engine => "/break_escape" +``` + +**Validation:** āœ… **MATCHES HACKTIVITY PATTERNS** + +**Evidence:** +```ruby +# From Hacktivity routes.rb (provided by user): +mount Resque::Server.new, at: "/resque" +# Same pattern āœ“ +``` + +**Path Structure:** +``` +https://hacktivity.com/break_escape/scenarios +https://hacktivity.com/break_escape/games/123/play +https://hacktivity.com/break_escape/api/games/123/bootstrap +``` + +**Recommendation:** āœ… Keep this approach + +--- + +## File Organization + +### āœ… Static Assets in public/ - CORRECT + +**Decision:** Move game files to `public/break_escape/` + +**Validation:** āœ… **CORRECT FOR RAILS ENGINES** + +**Structure:** +``` +public/break_escape/ +ā”œā”€ā”€ js/ (ES6 modules, unchanged) +ā”œā”€ā”€ css/ (stylesheets) +└── assets/ (images, sounds, Tiled maps) +``` + +**Reasoning:** +1. **Engine isolation** - Assets namespaced under /break_escape/ +2. **No asset pipeline** - Simpler deployment +3. **Direct serving** - Nginx/Apache can serve static files +4. **Phaser compatibility** - Expects direct asset URLs + +**Alternative Considered:** Rails asset pipeline +**Why Rejected:** +- Adds build complexity +- Phaser needs direct file paths +- No benefit for this use case + +**Recommendation:** āœ… Keep static assets in public/ + +--- + +### āš ļø Scenarios in app/assets/ - QUESTIONABLE + +**Decision:** Store scenarios in `app/assets/scenarios/` with ERB templates. + +**Validation:** āš ļø **UNCONVENTIONAL BUT ACCEPTABLE** + +**Reasoning:** +- `app/assets/` typically for CSS/JS/images +- ERB processing needed for randomization +- Not served directly to client (processed server-side) + +**Alternative:** `lib/break_escape/scenarios/` +**Why Not Chosen:** Not clear from plan + +**Concerns:** +1. Asset pipeline might try to process these files +2. Needs explicit exclusion from sprockets +3. Unconventional location + +**Mitigation:** +```ruby +# config/initializers/assets.rb +Rails.application.config.assets.exclude_paths << Rails.root.join("app/assets/scenarios") +``` + +**Recommendation:** āš ļø Consider moving to `lib/` or document exclusion + +--- + +## Client Integration Strategy + +### āœ… Minimal Changes - EXCELLENT + +**Decision:** Minimize client-side code rewrites. + +**Validation:** āœ… **CORRECT ENGINEERING PRACTICE** + +**Changes Required:** +1. **New files** (2): + - `config.js` - API configuration + - `api-client.js` - Fetch wrapper +2. **Modified files** (~5): + - Update scenario loading + - Update unlock validation + - Update NPC script loading + - Update inventory sync + - Update state persistence + +**What's NOT changed:** +- āœ… Game logic (player.js, rooms.js) +- āœ… Phaser scenes (game.js) +- āœ… Minigames (all minigame files) +- āœ… NPC systems (npc-manager.js, etc.) +- āœ… UI components (ui/, systems/) + +**Percentage of Code Changed:** <5% of client codebase + +**Recommendation:** āœ… Excellent approach - minimizes risk + +--- + +## ERB Template Usage + +### āœ… ERB for Randomization - CLEVER SOLUTION + +**Decision:** Use ERB templates for scenario randomization. + +**Example:** +```erb +{ + "locked": true, + "lockType": "password", + "requires": "<%= random_password %>" +} +``` + +**Validation:** āœ… **CREATIVE AND APPROPRIATE** + +**Strengths:** +1. **Simple randomization** - No complex logic needed +2. **Rails native** - No new dependencies +3. **Cacheable** - Generate once per game instance +4. **Secure** - Passwords server-side only + +**Concerns:** +1. **JSON syntax errors** - ERB could break JSON +2. **No syntax checking** - Until runtime +3. **Debugging harder** - Template vs output + +**Mitigation:** +```ruby +# Add JSON validation after ERB processing +def load + template_path = Rails.root.join('app/assets/scenarios', scenario_name, 'scenario.json.erb') + erb = ERB.new(File.read(template_path)) + output = erb.result(binding_context.get_binding) + + # Validate JSON syntax + JSON.parse(output) +rescue JSON::ParserError => e + raise "Scenario #{scenario_name} has invalid JSON after ERB processing: #{e.message}" +end +``` + +**Recommendation:** āœ… Keep ERB approach, add validation + +--- + +## Testing Strategy + +### āœ… Minitest with Fixtures - MATCHES HACKTIVITY + +**Decision:** Use Minitest (not RSpec) with fixture-based testing. + +**Validation:** āœ… **CORRECT FOR COMPATIBILITY** + +**Evidence from Hacktivity:** +```ruby +# test/models/user_test.rb +class UserTest < ActiveSupport::TestCase + should have_many(:events).through(:results) + should validate_presence_of(:handle) +end +``` + +**Plan Matches:** +```ruby +# test/models/break_escape/game_instance_test.rb +class GameInstanceTest < ActiveSupport::TestCase + test "should unlock room" do + game = game_instances(:active_game) + game.unlock_room!('room_office') + assert game.room_unlocked?('room_office') + end +end +``` + +**Recommendation:** āœ… Keep Minitest approach + +--- + +## Pundit Authorization + +### āœ… Pundit Policies - APPROPRIATE + +**Decision:** Use Pundit for authorization (not CanCanCan). + +**Validation:** āœ… **CLEAN AND TESTABLE** + +**Policy Example:** +```ruby +class GameInstancePolicy < ApplicationPolicy + def show? + record.player == user || user&.admin? + end +end +``` + +**Strengths:** +1. **Explicit policies** - Easy to understand +2. **Testable** - Unit test each policy +3. **Flexible** - Supports polymorphic associations +4. **Standard** - Well-documented gem + +**Hacktivity Compatibility:** +- User model has `admin?` method āœ… +- User model has `account_manager?` method āœ… +- Can extend for custom roles āœ… + +**Recommendation:** āœ… Keep Pundit + +--- + +## Content Security Policy + +### āœ… CSP with Nonces - SECURE + +**Decision:** Use CSP nonces for inline scripts. + +**Validation:** āœ… **MATCHES HACKTIVITY SECURITY** + +**Evidence from Hacktivity:** +```erb +<%= javascript_include_tag 'application', nonce: content_security_policy_nonce %> +``` + +**Plan Implementation:** +```erb + +``` + +**Security Benefits:** +1. **Prevents XSS** - Blocks unauthorized inline scripts +2. **Nonce-based** - Better than unsafe-inline +3. **Rails built-in** - No custom implementation + +**Recommendation:** āœ… Keep CSP approach + +--- + +## Summary: Architecture Score Card + +| Component | Rating | Notes | +|-----------|--------|-------| +| Database (JSONB) | āœ… Excellent | Perfect for game state | +| Polymorphic Player | āœ… Correct | Supports both modes | +| API Design | āœ… Good | 6 endpoints sufficient | +| NPC Schema | āš ļø Needs Fix | Shared NPCs issue | +| Routes/Mounting | āœ… Correct | Matches Hacktivity | +| Static Assets | āœ… Correct | public/ is right | +| Scenario Storage | āš ļø Questionable | Consider lib/ | +| Client Changes | āœ… Minimal | <5% code change | +| ERB Templates | āœ… Clever | Add validation | +| Testing | āœ… Correct | Matches Hacktivity | +| Authorization | āœ… Correct | Pundit is good | +| Security (CSP) | āœ… Correct | Nonces match | + +**Overall Architecture Grade: A-** (Would be A+ with NPC schema fix) + +--- + +**Next Document:** [03_MIGRATION_PLAN_REVIEW.md](./03_MIGRATION_PLAN_REVIEW.md) - Phase-by-phase implementation review diff --git a/planning_notes/rails-engine-migration-json/review1/05_RECOMMENDATIONS.md b/planning_notes/rails-engine-migration-json/review1/05_RECOMMENDATIONS.md new file mode 100644 index 0000000..a2a2a0c --- /dev/null +++ b/planning_notes/rails-engine-migration-json/review1/05_RECOMMENDATIONS.md @@ -0,0 +1,452 @@ +# Recommendations and Improvements + +This document provides prioritized recommendations for improving the migration plan. + +--- + +## Priority 0: Must-Fix Before Implementation (Blockers) + +These issues **WILL** cause implementation failures if not addressed. + +### P0-1: Fix Ink File Structure (Issue #1) + +**Problem:** Plan assumes .ink.json files, but codebase uses .json + +**Action Required:** +1. Update Phase 3 file movement commands to handle both patterns +2. Add compilation step before file reorganization +3. Update ScenarioLoader to check both file extensions + +**Files to Update:** +- `02_IMPLEMENTATION_PLAN.md` - Phase 3 commands +- `02_IMPLEMENTATION_PLAN.md` - Add Phase 2.5 for compilation + +**Time:** 2 hours +**Risk if skipped:** Phase 3 will fail with "file not found" errors + +--- + +### P0-2: Add Ink Compilation Pipeline (Issue #3) + +**Problem:** No documented process for compiling .ink → .json + +**Action Required:** +1. Document Inklecate installation +2. Create compilation script +3. Add Rake task for compilation +4. Add to deployment pipeline + +**Files to Create:** +- `scripts/compile_ink.sh` +- `lib/tasks/ink.rake` + +**Files to Update:** +- `02_IMPLEMENTATION_PLAN.md` - Add Phase 2.5 +- `04_TESTING_GUIDE.md` - Add compilation to CI + +**Time:** 4 hours +**Risk if skipped:** NPC scripts won't work, game will break + +--- + +### P0-3: Fix NPC Schema for Shared Scripts (Issue #2) + +**Problem:** Current schema doesn't support NPCs shared across scenarios + +**Action Required:** +1. Update database migration for shared NPC support +2. Add join table for scenario-npc relationships +3. Update ScenarioLoader to handle shared NPCs +4. Update models and associations + +**Files to Update:** +- `02_IMPLEMENTATION_PLAN.md` - Phase 4 migrations +- `03_DATABASE_SCHEMA.md` - Schema reference +- See detailed fix in `06_UPDATED_SCHEMA.md` + +**Time:** 4 hours +**Risk if skipped:** Seed script will fail or create duplicate data + +--- + +## Priority 1: Should-Fix Before Implementation (Quality) + +These issues won't block implementation but will cause bugs or data loss. + +### P1-1: Extend player_state Schema (Issue #4) + +**Problem:** Missing fields for minigame state + +**Action Required:** +Add to player_state JSONB: +```ruby +biometricSamples: [], +biometricUnlocks: [], +bluetoothDevices: [], +notes: [], +startTime: nil +``` + +**Files to Update:** +- `02_IMPLEMENTATION_PLAN.md` - Phase 4 migration +- `03_DATABASE_SCHEMA.md` - player_state structure +- `01_ARCHITECTURE.md` - Update examples + +**Time:** 1 hour +**Risk if skipped:** Minigame progress lost on page reload + +--- + +### P1-2: Clarify Room Asset Loading (Issue #5) + +**Problem:** Unclear how Tiled maps are served + +**Action Required:** +1. Document that Tiled maps remain static assets +2. Clarify API returns game logic only +3. Update client integration example + +**Files to Update:** +- `01_ARCHITECTURE.md` - API endpoint documentation +- `02_IMPLEMENTATION_PLAN_PART2.md` - Phase 9 client changes + +**Time:** 2 hours +**Risk if skipped:** Confusion during implementation, potential rework + +--- + +### P1-3: Add JSON Validation to ERB Processing + +**Problem:** ERB errors could produce invalid JSON + +**Action Required:** +```ruby +def load + # ... ERB processing ... + output = erb.result(binding_context.get_binding) + + # Validate JSON + JSON.parse(output) +rescue JSON::ParserError => e + raise "Invalid JSON in #{scenario_name}: #{e.message}" +end +``` + +**Files to Update:** +- `02_IMPLEMENTATION_PLAN.md` - Phase 5 ScenarioLoader code + +**Time:** 30 minutes +**Risk if skipped:** Hard-to-debug runtime errors + +--- + +## Priority 2: Nice-to-Have Improvements (Enhancements) + +These are optional improvements that would enhance the system. + +### P2-1: Add Minigame Results API Endpoint + +**Rationale:** Currently, minigame results are client-side only. + +**Proposed Addition:** +```ruby +# POST /api/games/:id/minigame_result +def minigame_result + authorize @game_instance + + minigame_name = params[:minigameName] + success = params[:success] + score = params[:score] + + # Track in player_state + @game_instance.player_state['minigameResults'] ||= [] + @game_instance.player_state['minigameResults'] << { + name: minigame_name, + success: success, + score: score, + timestamp: Time.current + } + @game_instance.save! + + render json: { success: true } +end +``` + +**Benefits:** +- Track player performance +- Analytics on minigame difficulty +- Leaderboards possible + +**Time:** 2 hours + +--- + +### P2-2: Add Scenario Versioning + +**Rationale:** Scenarios will evolve, need version tracking. + +**Proposed Addition:** +```ruby +create_table :break_escape_scenarios do |t| + # ... existing fields ... + t.string :version, default: '1.0.0' + t.datetime :published_at +end +``` + +**Benefits:** +- Track scenario updates +- Support A/B testing +- Rollback capability + +**Time:** 1 hour + +--- + +### P2-3: Add Game Instance Snapshots + +**Rationale:** Allow players to save/load game state. + +**Proposed Addition:** +```ruby +create_table :break_escape_game_snapshots do |t| + t.references :game_instance + t.jsonb :snapshot_data + t.string :snapshot_type # auto, manual + t.timestamps +end +``` + +**Benefits:** +- Auto-save every N minutes +- Manual save points +- Recover from bugs + +**Time:** 3 hours + +--- + +### P2-4: Add Performance Monitoring + +**Rationale:** Track API performance and errors. + +**Proposed Addition:** +```ruby +# lib/break_escape/performance_monitor.rb +module BreakEscape + class PerformanceMonitor + def self.track(action_name) + start = Time.current + result = yield + duration = Time.current - start + + Rails.logger.info "[BreakEscape] #{action_name} completed in #{duration}ms" + result + rescue => e + Rails.logger.error "[BreakEscape] #{action_name} failed: #{e.message}" + raise + end + end +end + +# Usage in controllers: +def bootstrap + PerformanceMonitor.track('bootstrap') do + # ... existing code ... + end +end +``` + +**Benefits:** +- Identify slow endpoints +- Track error rates +- Production debugging + +**Time:** 2 hours + +--- + +## Priority 3: Documentation Improvements + +### P3-1: Add Troubleshooting Guide + +**Content to Add:** +- Common installation issues +- Debugging NPC scripts +- Fixing migration errors +- Client-server sync issues + +**Location:** `planning_notes/rails-engine-migration-json/08_TROUBLESHOOTING.md` + +**Time:** 3 hours + +--- + +### P3-2: Add API Usage Examples + +**Content to Add:** +- cURL examples for each endpoint +- JavaScript fetch examples +- Error response formats +- Rate limiting info (if added) + +**Location:** `planning_notes/rails-engine-migration-json/09_API_EXAMPLES.md` + +**Time:** 2 hours + +--- + +### P3-3: Add Development Workflow Guide + +**Content to Add:** +- Setting up local development +- Creating new scenarios +- Testing workflow +- Debugging tips + +**Location:** `planning_notes/rails-engine-migration-json/10_DEV_WORKFLOW.md` + +**Time:** 2 hours + +--- + +## Priority 4: Testing Enhancements + +### P4-1: Add Integration Test for Complete Flow + +**Proposed Test:** +```ruby +# test/integration/break_escape/complete_game_flow_test.rb +class CompleteGameFlowTest < ActionDispatch::IntegrationTest + test "player can complete full scenario" do + user = users(:user) + sign_in user + + # 1. Select scenario + get scenarios_path + assert_response :success + + # 2. Start game + post scenario_path(scenarios(:ceo_exfil)) + follow_redirect! + game = assigns(:game_instance) + + # 3. Bootstrap + get api_game_bootstrap_path(game) + assert_response :success + data = JSON.parse(response.body) + assert_equal 'reception', data['startRoom'] + + # 4. Unlock room + post api_game_unlock_path(game), params: { + targetType: 'door', + targetId: 'office', + method: 'password', + attempt: 'correct_password' + } + assert_response :success + result = JSON.parse(response.body) + assert result['success'] + + # 5. Complete scenario + # ... test full flow ... + end +end +``` + +**Time:** 4 hours + +--- + +### P4-2: Add Performance Tests + +**Proposed:** +```ruby +# test/performance/break_escape/api_performance_test.rb +class ApiPerformanceTest < ActionDispatch::IntegrationTest + test "bootstrap endpoint responds in <200ms" do + game = game_instances(:active_game) + + benchmark = Benchmark.measure do + get api_game_bootstrap_path(game) + end + + assert benchmark.real < 0.2, "Bootstrap took #{benchmark.real}s (>200ms)" + end +end +``` + +**Time:** 2 hours + +--- + +## Implementation Priority Matrix + +| Priority | Items | Total Time | Start When | +|----------|-------|------------|------------| +| **P0** | 3 items | ~10 hours | Before Phase 1 | +| **P1** | 3 items | ~3.5 hours | Before Phase 4 | +| **P2** | 4 items | ~8 hours | After Phase 12 (post-launch) | +| **P3** | 3 items | ~7 hours | Parallel with implementation | +| **P4** | 2 items | ~6 hours | Phase 10 (Testing) | + +--- + +## Recommended Action Plan + +### Week 0: Pre-Implementation Fixes (10-14 hours) + +**Day 1-2:** +1. āœ… Fix Ink file structure (P0-1) - 2h +2. āœ… Add Ink compilation pipeline (P0-2) - 4h +3. āœ… Fix NPC schema (P0-3) - 4h + +**Day 3:** +4. āœ… Extend player_state schema (P1-1) - 1h +5. āœ… Clarify room asset loading (P1-2) - 2h +6. āœ… Add JSON validation (P1-3) - 0.5h + +**Output:** Updated planning documents ready for implementation + +--- + +### During Implementation: Parallel Tasks + +**While building Phases 1-6:** +- Write troubleshooting guide (P3-1) +- Write API examples (P3-2) +- Write dev workflow guide (P3-3) + +**During Phase 10 (Testing):** +- Add integration tests (P4-1) +- Add performance tests (P4-2) + +--- + +### Post-Launch: Enhancements + +**After Phase 12 (Deployment):** +- Minigame results endpoint (P2-1) +- Scenario versioning (P2-2) +- Game snapshots (P2-3) +- Performance monitoring (P2-4) + +--- + +## Summary + +**Must fix before starting:** 3 items (P0) +**Should fix before Phase 4:** 3 items (P1) +**Total pre-work:** ~14 hours (1.75 days) + +**With fixes applied:** +- āœ… Implementation will succeed +- āœ… No data loss +- āœ… No runtime errors +- āœ… Clean architecture + +**Timeline Impact:** +1.75 days prep, but saves 3-5 days of rework + +--- + +**Next Document:** [06_UPDATED_SCHEMA.md](./06_UPDATED_SCHEMA.md) - Corrected database schema with shared NPC support diff --git a/planning_notes/rails-engine-migration-json/review1/06_UPDATED_SCHEMA.md b/planning_notes/rails-engine-migration-json/review1/06_UPDATED_SCHEMA.md new file mode 100644 index 0000000..1e547e0 --- /dev/null +++ b/planning_notes/rails-engine-migration-json/review1/06_UPDATED_SCHEMA.md @@ -0,0 +1,561 @@ +# Updated Database Schema (Corrected) + +This document provides the corrected database schema that fixes Issue #2 (shared NPCs). + +--- + +## Problem Statement + +**Original Schema Issue:** +```ruby +create_table :break_escape_npc_scripts do |t| + t.references :scenario, null: false + t.string :npc_id, null: false + t.index [:scenario_id, :npc_id], unique: true # ← Forces 1:1 relationship +end +``` + +**Codebase Reality:** +- Many NPCs are shared across multiple scenarios +- `test-npc.json` used in 10+ scenarios +- `generic-npc.json`, hub NPCs are reusable + +**Result:** Schema doesn't match actual usage patterns. + +--- + +## Solution: Shared NPC Registry with Join Table + +--- + +## Migration 1: NPC Scripts (Shared Registry) + +```ruby +# db/migrate/xxx_create_break_escape_npc_scripts.rb +class CreateBreakEscapeNpcScripts < ActiveRecord::Migration[7.0] + def change + create_table :break_escape_npc_scripts do |t| + # Global NPC identifier (no scenario_id!) + t.string :npc_id, null: false + + # Display information + t.string :display_name + t.string :avatar_path + + # Ink scripts + t.text :ink_source # Optional .ink source + t.text :ink_compiled, null: false # Required .json compiled + + # Metadata + t.string :npc_type # 'phone', 'person', 'generic' + t.jsonb :default_config # Default timedMessages, eventMappings + + t.timestamps + end + + # Global registry - each NPC appears once + add_index :break_escape_npc_scripts, :npc_id, unique: true + add_index :break_escape_npc_scripts, :npc_type + end +end +``` + +--- + +## Migration 2: Scenario-NPC Join Table + +```ruby +# db/migrate/xxx_create_break_escape_scenario_npcs.rb +class CreateBreakEscapeScenarioNpcs < ActiveRecord::Migration[7.0] + def change + create_table :break_escape_scenario_npcs do |t| + # Foreign keys + t.references :scenario, null: false, foreign_key: { to_table: :break_escape_scenarios } + t.references :npc_script, null: false, foreign_key: { to_table: :break_escape_npc_scripts } + + # Scenario-specific overrides (optional) + t.jsonb :config_overrides # Override timedMessages, eventMappings for this scenario + t.string :initial_knot # Starting dialogue knot for this scenario + t.string :room_id # Which room NPC appears in + + t.timestamps + end + + # Many-to-many: scenario can have many NPCs, NPC can be in many scenarios + add_index :break_escape_scenario_npcs, [:scenario_id, :npc_script_id], unique: true, name: 'index_scenario_npcs_unique' + add_index :break_escape_scenario_npcs, :room_id + end +end +``` + +--- + +## Migration 3: Scenarios (Unchanged) + +```ruby +# db/migrate/xxx_create_break_escape_scenarios.rb +class CreateBreakEscapeScenarios < ActiveRecord::Migration[7.0] + def change + create_table :break_escape_scenarios do |t| + t.string :name, null: false + t.string :display_name, null: false + t.text :description + t.jsonb :scenario_data, null: false + t.boolean :published, default: false + t.integer :difficulty_level, default: 1 + + t.timestamps + end + + add_index :break_escape_scenarios, :name, unique: true + add_index :break_escape_scenarios, :published + add_index :break_escape_scenarios, :scenario_data, using: :gin + end +end +``` + +--- + +## Migration 4: Game Instances (Extended) + +```ruby +# db/migrate/xxx_create_break_escape_game_instances.rb +class CreateBreakEscapeGameInstances < ActiveRecord::Migration[7.0] + def change + create_table :break_escape_game_instances do |t| + # Polymorphic player + t.references :player, polymorphic: true, null: false + + # Scenario reference + t.references :scenario, null: false, foreign_key: { to_table: :break_escape_scenarios } + + # Player state (EXTENDED) + t.jsonb :player_state, null: false, default: { + currentRoom: nil, + position: { x: 0, y: 0 }, + unlockedRooms: [], + unlockedObjects: [], + inventory: [], + encounteredNPCs: [], + globalVariables: {}, + + # NEW: Minigame state + biometricSamples: [], + biometricUnlocks: [], + bluetoothDevices: [], + notes: [], + + # NEW: Minigame results + minigameResults: [], + + # NEW: Timestamps + startTime: nil, + lastSyncTime: nil + } + + # Game metadata + t.string :status, default: 'in_progress' + t.datetime :started_at + t.datetime :completed_at + t.integer :score, default: 0 + t.integer :health, default: 100 + + t.timestamps + end + + add_index :break_escape_game_instances, + [:player_type, :player_id, :scenario_id], + unique: true, + name: 'index_game_instances_on_player_and_scenario' + add_index :break_escape_game_instances, :player_state, using: :gin + add_index :break_escape_game_instances, :status + end +end +``` + +--- + +## Updated Models + +### NpcScript Model + +```ruby +# app/models/break_escape/npc_script.rb +module BreakEscape + class NpcScript < ApplicationRecord + self.table_name = 'break_escape_npc_scripts' + + # Many-to-many with scenarios + has_many :scenario_npcs, class_name: 'BreakEscape::ScenarioNpc', dependent: :destroy + has_many :scenarios, through: :scenario_npcs + + validates :npc_id, presence: true, uniqueness: true + validates :ink_compiled, presence: true + + # Return compiled Ink JSON + def compiled_json + JSON.parse(ink_compiled) + rescue JSON::ParserError + {} + end + end +end +``` + +--- + +### ScenarioNpc Model (New) + +```ruby +# app/models/break_escape/scenario_npc.rb +module BreakEscape + class ScenarioNpc < ApplicationRecord + self.table_name = 'break_escape_scenario_npcs' + + belongs_to :scenario, class_name: 'BreakEscape::Scenario' + belongs_to :npc_script, class_name: 'BreakEscape::NpcScript' + + # Merge NPC defaults with scenario-specific overrides + def effective_config + npc_script.default_config.deep_merge(config_overrides || {}) + end + end +end +``` + +--- + +### Scenario Model (Updated) + +```ruby +# app/models/break_escape/scenario.rb +module BreakEscape + class Scenario < ApplicationRecord + self.table_name = 'break_escape_scenarios' + + has_many :game_instances, class_name: 'BreakEscape::GameInstance' + + # Many-to-many with NPCs + has_many :scenario_npcs, class_name: 'BreakEscape::ScenarioNpc', dependent: :destroy + has_many :npc_scripts, through: :scenario_npcs + + validates :name, presence: true, uniqueness: true + validates :display_name, presence: true + validates :scenario_data, presence: true + + scope :published, -> { where(published: true) } + + # ... existing methods ... + + # Get NPCs for a specific room + def npcs_in_room(room_id) + scenario_npcs.where(room_id: room_id).includes(:npc_script) + end + end +end +``` + +--- + +### GameInstance Model (Extended) + +```ruby +# app/models/break_escape/game_instance.rb +module BreakEscape + class GameInstance < ApplicationRecord + self.table_name = 'break_escape_game_instances' + + belongs_to :player, polymorphic: true + belongs_to :scenario, class_name: 'BreakEscape::Scenario' + + validates :player, presence: true + validates :scenario, presence: true + validates :status, inclusion: { in: %w[in_progress completed abandoned] } + + scope :active, -> { where(status: 'in_progress') } + scope :completed, -> { where(status: 'completed') } + + before_create :set_started_at + before_create :initialize_player_state + + # ... existing methods ... + + # NEW: Minigame state management + def add_biometric_sample!(sample) + player_state['biometricSamples'] ||= [] + player_state['biometricSamples'] << sample + save! + end + + def add_bluetooth_device!(device) + player_state['bluetoothDevices'] ||= [] + player_state['bluetoothDevices'] << device unless player_state['bluetoothDevices'].any? { |d| d['mac'] == device['mac'] } + save! + end + + def add_note!(note) + player_state['notes'] ||= [] + player_state['notes'] << note + save! + end + + def record_minigame_result!(minigame_name, success, score = nil) + player_state['minigameResults'] ||= [] + player_state['minigameResults'] << { + name: minigame_name, + success: success, + score: score, + timestamp: Time.current.iso8601 + } + save! + end + + private + + def initialize_player_state + self.player_state ||= {} + self.player_state['currentRoom'] ||= scenario.start_room + self.player_state['unlockedRooms'] ||= [scenario.start_room] + self.player_state['position'] ||= { 'x' => 0, 'y' => 0 } + self.player_state['inventory'] ||= [] + self.player_state['unlockedObjects'] ||= [] + self.player_state['encounteredNPCs'] ||= [] + self.player_state['globalVariables'] ||= {} + + # NEW: Initialize minigame state + self.player_state['biometricSamples'] ||= [] + self.player_state['biometricUnlocks'] ||= [] + self.player_state['bluetoothDevices'] ||= [] + self.player_state['notes'] ||= [] + self.player_state['minigameResults'] ||= [] + + # NEW: Timestamps + self.player_state['startTime'] ||= Time.current.iso8601 + self.player_state['lastSyncTime'] = Time.current.iso8601 + end + end +end +``` + +--- + +## Updated ScenarioLoader + +```ruby +# lib/break_escape/scenario_loader.rb +module BreakEscape + class ScenarioLoader + attr_reader :scenario_name + + def initialize(scenario_name) + @scenario_name = scenario_name + end + + def import! + scenario_data = load_and_process_erb + + scenario = Scenario.find_or_initialize_by(name: scenario_name) + scenario.assign_attributes( + display_name: scenario_data['scenarioName'] || scenario_name.titleize, + description: scenario_data['scenarioBrief'], + scenario_data: scenario_data, + published: true + ) + scenario.save! + + # Import NPCs (shared registry) + import_npc_scripts!(scenario, scenario_data) + + scenario + end + + private + + def load_and_process_erb + template_path = Rails.root.join('app/assets/scenarios', scenario_name, 'scenario.json.erb') + raise "Scenario not found: #{scenario_name}" unless File.exist?(template_path) + + erb = ERB.new(File.read(template_path)) + binding_context = ScenarioBinding.new + output = erb.result(binding_context.get_binding) + + # Validate JSON + JSON.parse(output) + rescue JSON::ParserError => e + raise "Invalid JSON in #{scenario_name} after ERB processing: #{e.message}" + end + + def import_npc_scripts!(scenario, scenario_data) + # Clear existing associations + scenario.scenario_npcs.destroy_all + + # Process each room's NPCs + scenario_data['rooms']&.each do |room_id, room_data| + next unless room_data['npcs'] + + room_data['npcs'].each do |npc_data| + npc_id = npc_data['id'] + + # Find or create global NPC script + npc_script = import_or_find_npc_script(npc_id, npc_data) + + # Create scenario-npc association + scenario.scenario_npcs.create!( + npc_script: npc_script, + room_id: room_id, + initial_knot: npc_data['currentKnot'] || 'start', + config_overrides: { + 'timedMessages' => npc_data['timedMessages'], + 'eventMappings' => npc_data['eventMappings'] + }.compact + ) + end + end + end + + def import_or_find_npc_script(npc_id, npc_data) + # Check if NPC already exists globally + npc_script = NpcScript.find_by(npc_id: npc_id) + return npc_script if npc_script + + # Create new NPC script + # Look for compiled script (check both .json and .ink.json) + story_path = npc_data['storyPath'] + compiled_path = Rails.root.join(story_path) + + unless File.exist?(compiled_path) + # Try .ink.json extension + ink_json_path = compiled_path.to_s.gsub(/\.json$/, '.ink.json') + compiled_path = Pathname.new(ink_json_path) if File.exist?(ink_json_path) + end + + raise "NPC script not found: #{story_path}" unless File.exist?(compiled_path) + + # Look for source .ink file + ink_source_path = compiled_path.to_s.gsub(/\.(ink\.)?json$/, '.ink') + ink_source = File.exist?(ink_source_path) ? File.read(ink_source_path) : nil + + NpcScript.create!( + npc_id: npc_id, + display_name: npc_data['displayName'], + avatar_path: npc_data['avatar'], + npc_type: npc_data['npcType'] || 'generic', + ink_source: ink_source, + ink_compiled: File.read(compiled_path), + default_config: { + 'phoneId' => npc_data['phoneId'] + }.compact + ) + end + + class ScenarioBinding + def initialize + @random_password = SecureRandom.alphanumeric(8) + @random_pin = rand(1000..9999).to_s + end + + attr_reader :random_password, :random_pin + + def get_binding + binding + end + end + end +end +``` + +--- + +## Benefits of Updated Schema + +### āœ… Supports Shared NPCs +```ruby +# test-npc script exists once +npc = NpcScript.find_by(npc_id: 'test_npc') + +# Used in multiple scenarios +npc.scenarios.count # => 10 +``` + +### āœ… Reduces Database Size +- 30 unique NPCs vs 100+ duplicates +- Single update affects all scenarios + +### āœ… Scenario-Specific Customization +```ruby +# Global NPC has default config +npc.default_config # => { phoneId: 'player_phone' } + +# Scenario can override +scenario_npc.config_overrides # => { timedMessages: [...] } +scenario_npc.effective_config # => Merged config +``` + +### āœ… Complete Minigame State +```ruby +game.player_state['biometricSamples'] # Persisted āœ“ +game.player_state['bluetoothDevices'] # Persisted āœ“ +game.player_state['notes'] # Persisted āœ“ +``` + +--- + +## Migration from Old Schema (If Needed) + +If implementation already started with old schema: + +```ruby +# db/migrate/xxx_migrate_to_shared_npcs.rb +class MigrateToSharedNpcs < ActiveRecord::Migration[7.0] + def up + # 1. Create new tables + create_table :break_escape_npc_scripts_new do |t| + # ... new schema ... + end + create_table :break_escape_scenario_npcs do |t| + # ... join table ... + end + + # 2. Migrate data + BreakEscape::NpcScript.find_each do |old_npc| + # Create global NPC if doesn't exist + new_npc = BreakEscape::NpcScript.find_or_create_by!( + npc_id: old_npc.npc_id, + ink_compiled: old_npc.ink_compiled + # ... other fields ... + ) + + # Create join table entry + BreakEscape::ScenarioNpc.create!( + scenario_id: old_npc.scenario_id, + npc_script_id: new_npc.id + ) + end + + # 3. Drop old table + drop_table :break_escape_npc_scripts_old + end +end +``` + +--- + +## Summary + +**Changes:** +1. āœ… NPC scripts are now global (no scenario_id) +2. āœ… Join table links scenarios ↔ NPCs (many-to-many) +3. āœ… Scenario-specific overrides supported +4. āœ… Minigame state fields added to player_state +5. āœ… JSON validation in ScenarioLoader + +**Files to Update:** +- `02_IMPLEMENTATION_PLAN.md` - Phase 4 migrations +- `03_DATABASE_SCHEMA.md` - All schema documentation +- `01_ARCHITECTURE.md` - Model examples + +**Timeline Impact:** +0 days (fix during Phase 4, no delay) + +--- + +**Next Document:** [README.md](./README.md) - Review navigation guide diff --git a/planning_notes/rails-engine-migration-json/review1/README.md b/planning_notes/rails-engine-migration-json/review1/README.md new file mode 100644 index 0000000..329718e --- /dev/null +++ b/planning_notes/rails-engine-migration-json/review1/README.md @@ -0,0 +1,285 @@ +# Rails Engine Migration Plan - Review 1 + +**Date:** November 20, 2025 +**Status:** COMPLETE +**Recommendation:** Fix critical issues (P0) before implementation + +--- + +## šŸ“‹ Review Overview + +This review analyzes the Rails Engine migration plans in `planning_notes/rails-engine-migration-json/` and identifies critical issues that must be addressed before implementation begins. + +**Overall Assessment:** MOSTLY SOLID - REQUIRES CORRECTIONS + +The migration plan is well-structured and technically sound, but several critical discrepancies between the plan's assumptions and the actual codebase structure were discovered. + +--- + +## šŸ“š Review Documents + +Read in this order: + +### 1. Start Here +**[00_EXECUTIVE_SUMMARY.md](./00_EXECUTIVE_SUMMARY.md)** +- High-level findings +- Critical issues summary +- Overall recommendation +- Next steps + +**Read time:** 5 minutes + +--- + +### 2. Critical Issues (Must Read) +**[01_CRITICAL_ISSUES.md](./01_CRITICAL_ISSUES.md)** +- Issue #1: Ink file structure mismatch (CRITICAL) +- Issue #2: Shared NPC relationships (HIGH) +- Issue #3: Missing Ink compilation pipeline (CRITICAL) +- Issue #4: Incomplete global state tracking (MEDIUM) +- Issue #5: Room asset loading clarity (MEDIUM) + +**Read time:** 20 minutes +**Action required:** Understand blockers before implementation + +--- + +### 3. Architecture Validation +**[02_ARCHITECTURE_REVIEW.md](./02_ARCHITECTURE_REVIEW.md)** +- Database design validation āœ… +- API design review āœ… +- File organization assessment āš ļø +- Client integration strategy āœ… +- Security (CSP) validation āœ… + +**Read time:** 15 minutes +**Purpose:** Confirm technical decisions are sound + +--- + +### 4. Recommendations (Action Items) +**[05_RECOMMENDATIONS.md](./05_RECOMMENDATIONS.md)** +- **P0 (Must-Fix):** 3 items, ~10 hours +- **P1 (Should-Fix):** 3 items, ~3.5 hours +- **P2 (Nice-to-Have):** 4 items, ~8 hours +- **P3 (Documentation):** 3 items, ~7 hours +- **P4 (Testing):** 2 items, ~6 hours + +**Read time:** 15 minutes +**Purpose:** Understand what needs to be fixed and when + +--- + +### 5. Solution: Updated Schema +**[06_UPDATED_SCHEMA.md](./06_UPDATED_SCHEMA.md)** +- Corrected database schema for shared NPCs +- Extended player_state with minigame fields +- Updated models and associations +- Migration from old schema (if needed) + +**Read time:** 15 minutes +**Purpose:** See how to fix Issue #2 and #4 + +--- + +## 🚨 Critical Findings + +### Blockers (Must Fix Before Phase 1) + +1. **Ink File Structure Mismatch** + - Plan assumes `.ink.json` files + - Codebase uses `.json` files + - Only 3 of 30 NPCs have compiled scripts + - **Impact:** Phase 3 file reorganization will fail + +2. **Missing Ink Compilation** + - No documented compilation process + - No tooling for compiling .ink → .json + - **Impact:** NPC scripts won't work + +3. **Shared NPC Schema Issue** + - Schema forces 1:1 scenario-NPC relationship + - Codebase has many-to-many usage + - **Impact:** Seed script will fail or duplicate data + +**Total Fix Time:** ~10 hours (1.25 days) + +--- + +## āœ… Strengths of Current Plan + +1. **JSON-Centric Approach** - Excellent fit for game state +2. **Minimal Client Changes** - <5% code change required +3. **Hacktivity Compatibility** - Thoroughly validated +4. **Phased Implementation** - Clear milestones +5. **Comprehensive Documentation** - 8 detailed guides +6. **Security** - CSP nonces, Pundit authorization + +--- + +## šŸ“Š Risk Assessment + +**Without Fixes:** +- āŒ Implementation will fail at Phase 3 +- āŒ Seed script will fail at Phase 5 +- āŒ NPCs won't function (runtime errors) +- āŒ Minigame state will be lost +- āŒ Rework required: 3-5 days + +**With Fixes:** +- āœ… Clean implementation +- āœ… No data loss +- āœ… No runtime errors +- āœ… Matches codebase reality + +**Recommendation:** Fix P0 issues (10 hours) to save 3-5 days of rework + +--- + +## šŸŽÆ Action Plan + +### Week 0: Pre-Implementation Fixes (1.5-2 days) + +**Priority 0 (Blockers):** +1. āœ… Fix Ink file structure handling - 2 hours +2. āœ… Add Ink compilation pipeline - 4 hours +3. āœ… Fix NPC schema for shared scripts - 4 hours + +**Priority 1 (Quality):** +4. āœ… Extend player_state schema - 1 hour +5. āœ… Clarify room asset loading - 2 hours +6. āœ… Add JSON validation to ERB - 0.5 hours + +**Output:** Updated planning documents ready for Phase 1 + +--- + +### During Implementation + +**Phases 1-6:** Write documentation (P3) +**Phase 10:** Add tests (P4) +**Post-Launch:** Add enhancements (P2) + +--- + +## šŸ“ Files Requiring Updates + +### Planning Documents to Update: + +1. **`02_IMPLEMENTATION_PLAN.md`** + - Add Phase 2.5: Ink compilation + - Update Phase 3: File movement commands + - Update Phase 4: Database migrations + - Update Phase 5: ScenarioLoader code + +2. **`03_DATABASE_SCHEMA.md`** + - Update NPC schema (shared registry) + - Add join table documentation + - Extend player_state structure + +3. **`01_ARCHITECTURE.md`** + - Clarify room asset serving + - Update model examples + - Add minigame state tracking + +--- + +## šŸ”§ Implementation Checklist + +### Before Starting Phase 1: +- [ ] Read 00_EXECUTIVE_SUMMARY.md +- [ ] Read 01_CRITICAL_ISSUES.md +- [ ] Read 05_RECOMMENDATIONS.md +- [ ] Update 02_IMPLEMENTATION_PLAN.md with fixes +- [ ] Update 03_DATABASE_SCHEMA.md with new schema +- [ ] Create scripts/compile_ink.sh +- [ ] Test Ink compilation on 2-3 scenarios +- [ ] Verify all .ink files compile successfully +- [ ] Commit updated planning documents + +### After Fixes Complete: +- [ ] Re-review updated plans +- [ ] Validate fixes with team +- [ ] Begin Phase 1 with confidence + +--- + +## šŸ“ˆ Timeline Impact + +| Scenario | Timeline | Outcome | +|----------|----------|---------| +| **Without fixes** | 12 weeks + 3-5 days rework | Failed implementation, rework required | +| **With fixes** | +1.5 days prep + 12 weeks | Clean implementation, no rework | + +**Net Impact:** +1.5 days upfront, saves 3-5 days of rework +**Overall Timeline:** Still within 12-14 week estimate + +--- + +## šŸŽ“ Key Learnings + +1. **Always validate assumptions** - Plan assumptions must match codebase reality +2. **Check file conventions** - Naming patterns matter (.json vs .ink.json) +3. **Schema must match usage** - Database relationships should reflect actual data patterns +4. **Compilation is critical** - Document tooling for generated files +5. **State must be complete** - Track all game state, not just core mechanics + +--- + +## šŸ“¬ Review Metadata + +**Reviewer:** Claude (Automated Code Review) +**Review Date:** November 20, 2025 +**Review Duration:** ~2 hours +**Codebase Commit:** e9c73aa +**Documents Reviewed:** 8 files in `planning_notes/rails-engine-migration-json/` +**Code Files Analyzed:** 15+ JavaScript files, 24 scenario files, Hacktivity integration files + +**Review Method:** +- Static code analysis +- File structure inspection +- Pattern matching with grep/glob +- Schema comparison +- Documentation cross-reference + +**Confidence Level:** HIGH +All findings verified through direct codebase inspection. + +--- + +## šŸ™ Next Steps + +### For Implementation Team: + +1. **Review this document** - Understand critical issues +2. **Read recommendations** - Prioritize fixes +3. **Apply fixes** - Update planning documents +4. **Validate fixes** - Test compilation, check schema +5. **Begin implementation** - Start Phase 1 confidently + +### For Stakeholders: + +1. **Note timeline adjustment** - +1.5 days prep time +2. **Approve schema changes** - Review 06_UPDATED_SCHEMA.md +3. **Allocate time for fixes** - 10-14 hours before Phase 1 +4. **Expect success** - With fixes, implementation will succeed + +--- + +## šŸ“ž Questions? + +If you have questions about: +- **Critical issues** → Re-read 01_CRITICAL_ISSUES.md +- **Specific fixes** → See 05_RECOMMENDATIONS.md +- **Database schema** → See 06_UPDATED_SCHEMA.md +- **Architecture** → See 02_ARCHITECTURE_REVIEW.md + +--- + +**Status:** āœ… REVIEW COMPLETE +**Recommendation:** APPROVE WITH CORRECTIONS +**Next Action:** Apply P0 fixes, then begin implementation + +--- + +*This review was generated to improve the success rate of the Rails Engine migration by identifying and addressing critical issues before implementation begins.*