Added comprehensive planning docs: - 00_OVERVIEW.md: Project aims, philosophy, all decisions - 01_ARCHITECTURE.md: Complete technical design - 02_DATABASE_SCHEMA.md: Full schema reference with examples Key simplifications: - 2 tables instead of 3-4 - Files on filesystem, metadata in database - JIT Ink compilation - Per-instance scenario generation via ERB - Polymorphic player (User/DemoUser) - Session-based auth - Minimal client changes (<5%) Next: Implementation plan with step-by-step TODO list
10 KiB
BreakEscape Rails Engine Migration - Overview
Version: 2.0 (Simplified Approach) Date: November 2025 Status: Ready for Implementation
Project Aims
Transform BreakEscape from a standalone client-side game into a Rails Engine that:
- Integrates with Hacktivity - Mounts seamlessly into existing Hacktivity platform
- Supports Standalone Mode - Can run independently for development/testing
- Tracks Player Progress - Persists game state server-side
- Enables Randomization - Each game instance has unique passwords/pins
- Validates Critical Actions - Server-side validation for unlocks and inventory
- Maintains Client Code - Minimal changes to existing JavaScript game logic
- Scales Efficiently - Simple architecture, low database overhead
Core Philosophy
"Simplify, Don't Complicate"
- Files on filesystem, metadata in database
- 2 tables, not 10+
- Generate data when needed, not in advance
- JIT compilation, not build pipelines
- Move files, don't rewrite code
"Trust the Client, Validate What Matters"
- Client handles: Player movement, dialogue, minigames, UI
- Server validates: Unlocks, room access, inventory, critical state
- Server tracks: Progress, completion, achievements
Key Architectural Decisions
1. Database: 2 Simple Tables
Decision: Use only 2 tables with JSONB for flexible state storage.
Tables:
break_escape_missions- Scenario metadata onlybreak_escape_games- Player state + scenario snapshot
Rationale:
- JSONB perfect for hierarchical game state
- No need for complex relationships
- Easy to add new fields without migrations
- Matches game data structure naturally
Rejected Alternative: Normalized relational schema (10+ tables) Why: Over-engineering, slow iteration, complex queries
2. NPC Scripts: Files on Filesystem
Decision: No NPC database table. Serve .ink scripts directly from filesystem with JIT compilation.
Implementation:
- Source:
scenarios/ink/npc-name.ink(version controlled) - Compiled:
scenarios/ink/npc-name.json(generated on-demand) - Endpoint:
GET /games/:id/ink?npc=npc_name(compiles if needed)
Rationale:
- Compilation is fast (~300ms, benchmarked)
- No database bloat
- Edit .ink files directly
- No complex seed process
- No duplication across scenarios
Rejected Alternative: NPC registry table with join tables Why: Complexity, duplication, over-engineering
3. Scenario Data: Per-Instance Generation
Decision: Generate scenario JSON via ERB when game instance is created, store in game record.
Implementation:
# Template: app/assets/scenarios/ceo_exfil/scenario.json.erb
# Generated: game.scenario_data (JSONB)
# Each instance gets unique passwords/pins
Rationale:
- True randomization per player
- Different passwords for each game
- Scenario solutions never sent to client
- Simple to implement
Rejected Alternative: Shared scenario_data table Why: Can't randomize per player, requires complex filtering
4. Static Assets: Move to public/
Decision: Move game files to public/break_escape/ without modification.
Structure:
public/break_escape/
├── js/ (ES6 modules, unchanged)
├── css/ (stylesheets, unchanged)
└── assets/ (images, sounds, Tiled maps, unchanged)
Rationale:
- No asset pipeline complexity
- Direct URLs for Phaser
- Engine namespace isolation
- Simple deployment
Rejected Alternative: Rails asset pipeline Why: Unnecessary complexity for Phaser game
5. Authentication: Polymorphic Player
Decision: Use polymorphic belongs_to :player for User or DemoUser.
Modes:
- Mounted (Hacktivity): Uses existing User model via Devise
- Standalone: Uses DemoUser model for development
Rationale:
- Supports both use cases
- Standard Rails pattern
- Authorization works naturally
- No special-casing in code
Rejected Alternative: User-only with optional flag Why: Tight coupling to Hacktivity, harder to develop standalone
6. API Design: Session-Based
Decision: Use Rails session authentication (not JWT).
Endpoints:
GET /games/:id/scenario - Get scenario JSON
GET /games/:id/ink?npc=... - Get NPC script (JIT compiled)
GET /api/games/:id/bootstrap - Initial game data
PUT /api/games/:id/sync_state - Sync player state
POST /api/games/:id/unlock - Validate unlock attempt
POST /api/games/:id/inventory - Update inventory
Rationale:
- Matches Hacktivity's Devise setup
- CSRF protection built-in
- Simpler than JWT
- Web-only use case
Rejected Alternative: JWT tokens Why: Adds complexity without benefit
7. File Organization: Cautious Approach
Decision: Use mv commands to reorganize, minimize code changes.
Process:
- Use
rails generatefor boilerplate - Use
mvto relocate files (not copy) - Edit only what's necessary
- Test after each phase
- Commit working state
Rationale:
- Preserves git history
- Avoids introducing bugs
- Clear audit trail
- Reversible steps
8. Client Integration: Minimal Changes
Decision: Add ~2 new files, modify ~5 existing files.
New Files:
config.js- API configurationapi-client.js- Fetch wrapper
Modified Files:
- Scenario loading (use API)
- Unlock validation (call server)
- NPC script loading (use API)
- Inventory sync (call server)
- Main game initialization
Rationale:
- <5% code change
- Reduces risk
- Preserves game logic
- Faster implementation
Rejected Alternative: Rewrite to use API throughout Why: Unnecessary, high risk, no benefit
9. Testing: Minitest with Fixtures
Decision: Use Minitest (matches Hacktivity) with fixture-based tests.
Test Types:
- Model tests (validations, methods)
- Controller tests (authorization, responses)
- Integration tests (full game flow)
Rationale:
- Matches Hacktivity's test framework
- Consistent testing approach
- Fixtures easier for game state
- Well-documented pattern
Rejected Alternative: RSpec Why: Different from Hacktivity, adds dependency
10. Authorization: Pundit Policies
Decision: Use Pundit for authorization (matches Hacktivity).
Policies:
- GamePolicy - Player can only access their own games
- MissionPolicy - Published scenarios visible to all
- Admin overrides for management
Rationale:
- Explicit, testable policies
- Flexible for complex rules
- Standard gem
- Matches Hacktivity
Rejected Alternative: CanCanCan Why: Less explicit, harder to test
Timeline and Scope
Estimated Duration: 10-12 weeks
Phase Breakdown:
- Setup Rails Engine (Week 1) - 1 week
- Move Game Files (Week 1) - 1 week
- Reorganize Scenarios (Week 1-2) - 1 week
- Database Setup (Week 2-3) - 1 week
- Models and Logic (Week 3-4) - 1 week
- Controllers and Routes (Week 4-5) - 2 weeks
- API Implementation (Week 5-6) - 2 weeks
- Client Integration (Week 7-8) - 2 weeks
- Testing (Week 9-10) - 2 weeks
- Standalone Mode (Week 10) - 1 week
- Deployment (Week 11-12) - 2 weeks
Total: 10-12 weeks
Success Criteria
Must Have (P0)
- ✅ Game runs in Hacktivity at
/break_escape - ✅ Player progress persists across sessions
- ✅ Unlocks validated server-side
- ✅ Each game instance has unique passwords
- ✅ NPCs work with dialogue
- ✅ All 24 scenarios loadable
- ✅ Standalone mode works for development
Should Have (P1)
- ✅ Integration tests pass
- ✅ Authorization policies work
- ✅ JIT Ink compilation works
- ✅ Game state includes all minigame data
- ✅ Admin can manage scenarios
- ✅ Error handling graceful
Nice to Have (P2)
- Performance monitoring
- Leaderboards
- Save/load system
- Scenario versioning
- Analytics tracking
Risk Mitigation
Risk: Breaking Existing Game Logic
Mitigation:
- Minimal client changes (<5%)
- Phased implementation with testing
- Preserve git history with mv
- Integration tests for game flow
Risk: Performance Issues
Mitigation:
- JIT compilation benchmarked (~300ms)
- JSONB with GIN indexes
- Static assets served directly
- Simple database queries
Risk: Complex State Management
Mitigation:
- JSONB for flexible state
- Server validates only critical actions
- Client remains authoritative for movement/UI
- Clear state sync strategy
Risk: Hacktivity Integration Issues
Mitigation:
- Validated against actual Hacktivity code
- Uses same patterns (Devise, Pundit, Minitest)
- Polymorphic player supports both modes
- CSP nonces for security
What's Different from Original Plan?
Simplified
Before: 3-4 tables (scenarios, npc_scripts, scenario_npcs, games) After: 2 tables (missions, games)
Before: Complex NPC registry with join tables After: Files on filesystem, JIT compilation
Before: Shared scenario_data in database After: Per-instance generation via ERB
Before: Pre-compilation build pipeline After: JIT compilation on first request
Before: 10-14 hours P0 prep work After: 0 hours P0 prep work
Results
- 50% fewer tables
- No complex relationships
- No build infrastructure
- Simpler seed process
- Better randomization
- Easier development
Documentation Structure
This migration plan consists of:
- 00_OVERVIEW.md (this file) - Aims, philosophy, decisions
- 01_ARCHITECTURE.md - Technical design details
- 02_DATABASE_SCHEMA.md - Complete schema reference
- 03_IMPLEMENTATION_PLAN.md - Step-by-step TODO list
- 04_API_REFERENCE.md - API endpoint documentation
- 05_TESTING_GUIDE.md - Testing strategy and examples
- 06_HACKTIVITY_INTEGRATION.md - Integration instructions
- README.md - Quick start and navigation
Quick Start
To begin implementation:
- Read this overview
- Read
01_ARCHITECTURE.mdfor technical details - Read
02_DATABASE_SCHEMA.mdfor database design - Start with
03_IMPLEMENTATION_PLAN.mdPhase 1 - Follow the step-by-step instructions
- Test after each phase
- Commit working state
Questions? Each document has detailed rationale and examples.
Summary
This migration transforms BreakEscape into a Rails Engine using the simplest possible approach:
- 2 database tables
- Files on filesystem
- JIT compilation
- Minimal client changes
- Standard Rails patterns
Ready to start! Proceed to 03_IMPLEMENTATION_PLAN.md for the step-by-step guide.