Flummox‽ing: A Comprehensive Project Plan for an AI-Integrated, Full-Stack Web Puzzler
Part 1: Foundational Strategy & Core Design
This section establishes the philosophical and architectural bedrock of the project, ensuring that all subsequent technical decisions are aligned with the core game design and player experience goals.
Section 1.1: Game Concept and Core Mechanics
Defining ‘Flummox‽ing’
The game is conceptualized as a 2.5D physics-based puzzler. Its design philosophy diverges from linear puzzle-solving, drawing inspiration from titles celebrated for their non-linear discovery and emergent gameplay, such as The Witness and Inside. The core gameplay loop involves players constructing and manipulating intricate, Rube Goldberg-esque contraptions. The game world operates under a consistent and predictable physics model, but the interaction of components is designed to produce complex, surprising, and often humorous outcomes.
The narrative will not be delivered through explicit cutscenes or dialogue but will unfold environmentally. Clues to the world’s story and the purpose of the player’s actions will be embedded within the level design, rewarding curiosity and exploration. This approach encourages players to form their own interpretations and fosters a deeper sense of immersion and mystery.
Core Mechanics
The primary mode of player interaction is physics-based manipulation—placing, rotating, and connecting various components to solve puzzles. This indirect control scheme shifts the player’s focus from character dexterity to strategic thinking and experimentation. The game will be presented from a 2.5D perspective, a technique seen in games like The Swapper and Little Nightmares 2, which uses 3D assets to create visually deep and atmospheric environments while constraining gameplay to a single, understandable 2D plane.1 This visual style is particularly well-suited for complex physics puzzles, as it prevents the ambiguity of depth perception from interfering with the precise placement of objects, a common challenge in full 3D physics games.
Section 1.2: The Player Experience – Designing for Engagement and “Positive Failure”
The player experience is paramount and will be guided by the principle of “Positive Failure,” where failing is not a punishment but a crucial and engaging part of the learning process.
The Principle of Fair Challenge
Central to the design is the principle that every challenge must feel winnable and that failure should be a direct, understandable consequence of the player’s actions, presenting a clear learning opportunity. Drawing from the design ethos of games like Dark Souls, where even high difficulty is perceived as fair, puzzle mechanics in ‘Flummox‽ing’ will be based on predictable patterns and consistent physical laws. This rewards persistence, observation, and mastery over randomness or trial-and-error.
Implementing “Positive Failure”
To cultivate an environment where players are encouraged to experiment without fear of punitive failure, several design tenets will be strictly followed:
- Immediate and Clear Feedback: Failure states, such as the collapse of a contraption, and their root causes will be made immediately obvious through clear visual and auditory cues. The game will avoid ambiguous failure conditions that leave the player guessing what went wrong, which is a primary source of frustration. This rapid feedback loop enables players to assess their strategy, adjust it, and re-attempt the puzzle with new knowledge.
- Entertaining Fail Sequences: Inspired by games like Super Monkey Ball 2, where the fail state itself is a source of amusement, the collapse of a player’s machine will be designed to be an entertaining spectacle. These sequences make the frequent failures inherent in a puzzle game as enjoyable as the occasional successes, reducing frustration and encouraging bold experimentation.
- Balanced Difficulty Curve: The game will begin with simpler challenges that serve to introduce core mechanics and physical principles organically. The complexity will then ramp up steadily, a design principle intended to keep players at the “edge” of their abilities—a state of high engagement where they are challenged but not overwhelmed. This prevents both boredom from tasks that are too easy and frustration from challenges that feel insurmountable.
Section 1.3: High-Level Technical Architecture Overview
The following section provides a summary of the full-stack technology choices for ‘Flummox‽ing’. Each component has been selected to meet the project’s specific requirements for high performance, scalability, and a streamlined, modern developer experience. The entire stack is built upon the TypeScript ecosystem to ensure type safety, reduce runtime errors, and enable code-sharing potential across the frontend and backend.
A key driver of the architectural design is the “Positive Failure” principle. To effectively balance the game and ensure players are learning rather than becoming frustrated, it is necessary to measure their progress with precision. This requires tracking detailed in-game events to derive novel metrics such as “Average Time-to-Epiphany” (the duration from a player’s first attempt to their successful solution) and “Rube Goldberg Efficiency” (a comparison of the number of components used by the player versus an optimal solution). Capturing and processing these metrics in real-time is not feasible with traditional batch-processing analytics systems. This requirement mandates a low-latency, real-time data pipeline, connecting a core game design principle directly to the mandatory inclusion of a specific backend subsystem featuring Socket.IO and a database schema capable of handling high-frequency event logging. This demonstrates a direct causal link between the desired player experience and the necessary system architecture.
Table 1: Flummox‽ing Technology Stack Summary
This table serves as a high-level executive summary, providing all stakeholders with a clear, concise, and justified overview of the project’s technical foundation. It immediately answers the “what” and “why” of our technology choices.
Category | Technology | Version/Provider | Purpose & Rationale |
Frontend Framework | Phaser 3 | Latest | A mature, feature-rich 2D game framework with a strong community. Ideal for building performant, browser-based games. |
Frontend Build Tool | Vite | Latest | Provides an extremely fast Hot Module Replacement (HMR) development server and highly optimized production builds, crucial for rapid iteration and performance. |
Physics Engine | Rapier.js | Latest | A high-performance 2D physics engine written in Rust and compiled to WebAssembly. Offers superior performance and stability over older JavaScript-based physics engines. |
Language | TypeScript | Latest | Ensures type safety across the entire full-stack application, significantly reducing runtime errors and improving code maintainability and refactorability in a large project. |
Backend Runtime | Node.js | LTS | The standard for building high-performance, scalable, and non-blocking I/O applications in JavaScript, allowing for code and skill reuse from the frontend team. |
Backend Framework | Express.js | Latest | A minimal and flexible Node.js web application framework that provides a robust set of features for building the game’s API and real-time services. |
Database | PlanetScale (MySQL) | Serverless | A highly scalable, serverless MySQL-compatible platform that handles sharding and schema migrations without downtime or locking, ideal for a game with potentially fluctuating user loads. |
ORM | Prisma | Latest | A next-generation Object-Relational Mapper (ORM) that provides a fully type-safe database client and declarative schema migrations, simplifying database interactions and reducing errors. |
Real-time Comms | Socket.IO | Latest | Enables real-time, bidirectional, and event-based communication between the server and client, essential for the live analytics dashboard and other potential real-time features. |
Part 2: The AI-Integrated Development Workflow
This part details a prescriptive and structured workflow that treats Artificial Intelligence not as a magic black box, but as an integral, manageable part of the development team. The goal is to maximize AI’s efficiency gains in drafting code while maintaining strict human oversight, accountability, and overall code quality.
Section 2.1: The #ai-draft -> #human-review Git Workflow
Core Philosophy
The foundational philosophy of this workflow is that AI is a powerful assistant, not a replacement for the developer. The process is explicitly designed to leverage AI tools like GitHub Copilot for accelerating development and reducing boilerplate, thereby empowering human developers to dedicate their cognitive resources to higher-order tasks such as architecture, complex business logic, and final accountability. Within this model, any code generated by an AI is treated as a “first pass” or an initial draft that always requires rigorous human validation and ownership before being integrated into the main codebase.
Step-by-Step Workflow
- Task Breakdown (Human): A developer receives a feature requirement (e.g., “Implement player authentication”). They first break this down into the smallest possible, self-contained vertical slice that delivers value, such as “Implement the user registration endpoint”. This granular approach is critical for effective AI assistance, as LLMs perform best on well-defined, small-scope tasks.
- Branch Creation (Human): The developer creates a new feature branch from the main branch. A strict naming convention is enforced to signal the nature of the work: feature/ai-draft-<feature-name>. For example: feature/ai-draft-registration-endpoint. This convention provides immediate context to the rest of the team that the initial commits on this branch will be AI-generated and require careful review.
- Prompting & Generation (Human/AI): The developer uses an integrated AI assistant (e.g., OpenAI Codex via GitHub Copilot) to generate the initial code draft. This is done by providing a detailed, structured prompt following the guidelines in the AI Prompt Codex (see Section 2.2). This step can generate initial models, API routes, controller logic, and basic test structures. The developer then commits this AI-generated code with a specific, conventional commit message format: feat(api): ai-draft user registration endpoint and initial tests.
- Refinement & Validation (Human): The developer’s role now shifts to that of the first-pass reviewer and owner. They must thoroughly review, refactor, and augment the AI’s output. This involves debugging the generated code, ensuring it adheres to the project’s architectural standards and business logic, and adding necessary complexity or nuance that the AI may have missed. The developer runs all relevant local tests to validate functionality. Commits made during this stage follow standard conventions, for example: feat(api): add password hashing and validation to registration.
- Pull Request (Human): Once the feature is complete and locally tested, the developer opens a Pull Request (PR) to merge their branch into the main branch. The PR title must be clear and descriptive, such as feat(api): Implement user registration endpoint. The PR description is non-negotiable and must follow a template that includes a summary of the changes, manual testing steps, and a mandatory checklist item: [x] I have reviewed, validated, and taken full ownership of all AI-generated code in this Pull Request. This checkbox serves as a formal declaration of accountability.
- Automated Review (CI/AI): The creation of the PR triggers a GitHub Actions workflow. As part of this CI pipeline, an AI-powered code reviewer tool, such as CodeRabbit, performs an automated first-pass review. This AI reviewer is configured to comment on the PR with suggestions related to potential bugs, style guide violations, and common security vulnerabilities. This provides immediate, low-level feedback to the author, offloading repetitive checks from human reviewers.
- Human Review (Human): A second developer, the designated human reviewer, then conducts the formal code review. Their focus is deliberately shifted away from minor style nits or simple syntax errors, as the automated AI reviewer should have already flagged these. Instead, the human reviewer concentrates on higher-level concerns: architectural soundness, logical correctness of the business logic, scalability implications, and overall alignment with the project’s goals. They are reviewing the entire PR, holding the author accountable for all code within it, irrespective of its origin.
- Merge & Cleanup (Human): After the PR is approved by the human reviewer and all CI checks have passed, it is merged into the main branch using a squash merge to maintain a clean history. The feature branch is then deleted from the remote repository.
Section 2.2: The AI Prompt Codex
Rationale
A standardized “codex” of prompt templates is essential for generating consistent, high-quality, and predictable code from AI assistants. An unstructured, ad-hoc approach to prompting leads to highly variable and often unusable results. This codex establishes a set of best practices that ensure the AI is given sufficient context, clarity, and constraints to produce useful output. The core principles are: be specific, provide context before instruction, articulate the desired format, and use “leading words” to guide the model.
Prompting Best Practices
- Assign a Persona: Always begin a prompt by assigning a role to the AI. This primes the model to access the most relevant parts of its training data. For example: “You are an expert Node.js developer specializing in secure and performant REST APIs using Express and Prisma”.
- Provide Context First: Before giving an instruction, provide all relevant context. This includes existing code snippets, file structures, database schemas, or related functions. Use markdown code blocks or delimiters like “”” to clearly separate contextual information from the prompt’s instruction.
- Break Down Complex Tasks: Never ask the AI to perform a large, multi-faceted task like “build the entire user feature.” This is a recipe for failure. Instead, break the feature down into its smallest logical, atomic components and prompt for each one sequentially. For example: “First, generate the Prisma model for User.” Then, in a separate prompt: “Next, generate the Express route for POST /api/v1/users”.
- Specify Constraints and Patterns: Explicitly state any technical constraints or design patterns that must be followed. For example: “Use the bcrypt library with a salt round of 12 for password hashing,” or “Ensure all database queries related to financial transactions are performed within a single Prisma transaction to maintain atomicity”.
Table 2: Sample AI Prompt Codex
This table provides a ready-to-use library of prompt templates for the development team. It ensures consistency, reduces the cognitive load of writing prompts from scratch, and codifies best practices for interacting with the AI assistant.
Task Category | Prompt Template | Example Usage |
Backend: Prisma Model | You are an expert database architect designing a schema for a MySQL-compatible database. Given the following requirements, generate a Prisma schema model for ‘[ModelName]’. The model must include these fields: [List fields with types, attributes like @id, @unique, @default, and relations]. Use camelCase for field names. Requirements:… | …generate a Prisma schema model for ‘Player’. Requirements: id (String, @id, @default(cuid())), username (String, @unique), email (String, @unique), hashedPassword (String), createdAt (DateTime, @default(now())), puzzleAttempts (relation to PuzzleAttempt). |
Backend: Express Controller | You are an expert Node.js developer. Write a controller function for an Express.js application using TypeScript. The function should be named ‘[functionName]’ and handle a request to ‘[routePath]’. It must: 1. Validate the request body using this Zod schema: “””[Paste Zod schema]”””. 2. Call the ‘[serviceFunctionName]’ service function with the validated data. 3. Respond with a status and the result from the service. 4. Implement try-catch for error handling and pass errors to the ‘next’ function. | …controller function named ‘createPlayer’. It handles a POST to ‘/api/v1/players’. It must validate against the ‘createPlayerSchema’, call the ‘playerService.create’ function, respond with 201, and use try-catch. |
Frontend: Phaser Scene | You are an expert Phaser 3 game developer using TypeScript. Create a new Scene class named ” that extends ‘Phaser.Scene’. The scene key must be ‘[sceneKey]’. It must have ‘preload’, ‘create’, and ‘update’ methods. In the ‘preload’ method, load the following assets using the appropriate loader functions: [List asset keys, types, and paths]. | …Create Scene class ‘LevelOneScene’, key ‘LevelOne’. In ‘preload’, load: this.load.image(‘sky’, ‘assets/images/sky.png’); this.load.atlas(‘player’, ‘assets/atlases/player.png’, ‘assets/atlases/player.json’); |
Testing: Vitest Unit Test | You are a QA engineer specializing in unit testing with Vitest. Write comprehensive unit tests for the following TypeScript function. Use ‘describe’ to group the tests and ‘it’ for individual cases. Use ‘vi.fn()’ to mock any dependencies. Cover the following scenarios: [List test cases, including happy path, edge cases, and error conditions]. The function to test is: “””[Paste function code]””” | …Write Vitest unit tests for the ‘calculateScore’ function. Cover scenarios: a successful calculation with a positive score, a score of zero, a score with a bonus multiplier, and an input with invalid data that should throw an error. |
Section 2.3: AI-Powered Code Review and Quality Gating
Role of AI in Review
In this workflow, AI code reviewers function as highly sophisticated, context-aware linters that are integrated directly into the CI pipeline. Their primary role is to act as a quality gate, providing a rapid, automated first pass on every pull request. They are configured to flag a wide range of common issues, including syntax errors, deviations from the established style guide, potential bugs (like null pointer exceptions or race conditions), and low-hanging security vulnerabilities (such as hardcoded secrets or use of insecure functions).
Tool Integration and Configuration
The project will integrate a dedicated GitHub Action for AI code review, such as CodeRabbit (coderabbitai/ai-pr-reviewer), which is well-regarded for this purpose. Alternatives include other marketplace actions or a custom-built action leveraging a model API. This action will be configured in the main CI workflow file to trigger on the pull_request event.
Key configurations for the tool will include:
- Incremental Reviews: The AI will be set to perform incremental reviews on each new commit pushed to the PR, rather than re-reviewing the entire changeset each time. This significantly reduces noise in the PR comments and minimizes API costs.
- Dual-Model Strategy: Where possible, the tool will be configured to use a more cost-effective model (e.g., gpt-3.5-turbo) for high-level tasks like generating PR summaries, and a more powerful, “heavy” model (e.g., gpt-4) for the detailed, line-by-line code analysis and comment generation.
- Ignoring Paths: The configuration will exclude certain files and directories from review, such as documentation (**/*.md), configuration files (**/*.json), and generated lockfiles, to focus the AI’s analysis on source code.
The Human-in-the-Loop Process
The AI-generated comments are treated as suggestions, not as infallible directives. The PR author is responsible for reviewing the AI’s feedback. They must critically assess each suggestion, accept and implement the ones that are valid by pushing subsequent commits, and dismiss those that are irrelevant or incorrect. This process ensures that the human developer remains in control. The ultimate benefit is that the subsequent human reviewer’s job is made significantly more efficient. They can proceed with the confidence that most low-level, stylistic, and common-error checks have already been performed, allowing them to dedicate their full attention to the more complex and nuanced architectural, logical, and strategic aspects of the code change.
Part 3: Full-Stack Technical Specifications
This part provides a detailed technical blueprint for the application’s architecture, covering the client-side rendering engine and physics simulation, as well as the server-side business logic and data persistence layer.
Section 3.1: Frontend Architecture (Phaser 3 & Vite)
Project Scaffolding and Structure
The frontend project will be initialized using the phaser3-typescript-vite-template. This template provides a modern, high-performance starting point with TypeScript, ESLint for code linting, and a pre-configured Vite development server, which is known for its extremely fast hot-reloading capabilities.
The project will adhere to the following directory structure to ensure maintainability and clear separation of concerns:
- public/: This directory is designated for all static assets, including images, audio files, fonts, and the JSON files for texture atlases. Files in this directory are served from the root and are not processed by Vite, making it the ideal location for game assets.
- src/: This directory contains all TypeScript source code.
- main.ts: The primary entry point for the game application. This file initializes the Phaser Game instance with the global configuration.
- scenes/: A dedicated directory for all Phaser Scene files (e.g., PreloaderScene.ts, GameScene.ts). This organization is a standard practice for managing different states or screens of a game.
- components/: This will house reusable game components or classes that are not Scenes themselves, such as a custom UI Button class or a complex game object controller.
- types/: A central location for shared TypeScript type definitions and interfaces, promoting consistency across the codebase.
- styles/: Contains CSS files, which can be imported directly into TypeScript components thanks to Vite’s capabilities.
Scene Management
The game’s architecture will be built around a system of multiple, self-contained Scenes, which are Phaser’s way of encapsulating a game world. Key scenes will include:
- PreloaderScene: Responsible for loading essential assets for the loading screen itself and displaying a progress bar.
- MainMenuScene: The main menu of the game.
- GameScene: The core scene where the actual puzzle gameplay occurs.
- UIScene: A dedicated scene rendered on top of the GameScene. This is a crucial architectural pattern that decouples the game’s UI (like score, timers, pause menu) from the game logic itself, making both easier to manage and debug.
State Management
For simple state that needs to be shared across scenes, such as the current player’s score or settings, the global Game Registry (this.registry) will be utilized. This is a built-in Phaser feature designed for this purpose. For more complex application-wide state that might be shared between the game and other UI elements (like an admin dashboard), a lightweight, dedicated state management solution will be implemented to avoid prop-drilling and maintain a single source of truth.
Section 3.2: Physics and Interaction
Physics Engine Integration
The Rapier.js physics engine will be integrated to handle all physics simulations. Its high performance, stemming from its Rust and WebAssembly foundation, makes it an ideal choice for a web-based game with potentially complex physics interactions. Integration will be streamlined by using the @phaserjs/rapier-connector plugin, which provides a developer-friendly API to bridge Phaser and Rapier.2
Initialization and World Creation
The create method of the GameScene will be declared as async. This is a critical requirement because the Rapier library must be initialized asynchronously via await RAPIER.init() before any physics world or bodies can be created. Failing to do so would result in runtime errors. After initialization, the physics world will be created with this.rapierPhysics = createRapierPhysics(gravity, this).
Rigid Body Management
Game objects in Phaser will be endowed with physical properties using the this.rapierPhysics.addRigidBody(gameObject, options) method. A clear distinction will be made between body types:
- RAPIER.RigidBodyType.Dynamic: Used for all movable puzzle pieces that are affected by gravity, forces, and collisions.
- RAPIER.RigidBodyType.Fixed: Used for all static level geometry, such as platforms and walls, which do not move.
Unified Input Handling
Phaser’s unified input system will be leveraged to handle both mouse and touch events seamlessly, ensuring a consistent experience on both desktop and mobile devices. Game objects intended for player interaction will be enabled with gameObject.setInteractive(). The core interaction mechanic—dragging and dropping physics objects—will be implemented by listening for pointerdown, pointermove, and pointerup events on these interactive objects. For simplicity and to support the primary single-pointer interaction model, scene.input.activePointer will be used to track the user’s input coordinates.
Section 3.3: Backend Architecture (Node.js & Express)
Project Structure
To ensure scalability and maintainability, the backend will adopt a clean, modular folder structure that strictly separates concerns:
- src/api/: The main container for all API-related code.
- routes/: Defines all API endpoints (e.g., player.routes.ts) and maps them to the appropriate controller functions.
- controllers/: Contains the business logic for each route. Controllers orchestrate requests by calling services and formatting responses.
- services/: Handles all interactions with the database (via the Prisma client) and any external services. This abstracts data access logic from the controllers.
- middlewares/: A dedicated location for Express middleware, such as authentication checks, request validation, and centralized error handling.
- utils/: A collection of helper functions, constants, and other shared utilities.
API Design and Middleware
The backend will expose a versioned REST API, with all routes prefixed (e.g., /api/v1/players), to allow for future iterations without breaking existing clients.
A robust middleware chain will process all incoming requests:
- Security Middleware: Helmet.js will be used to automatically apply crucial security-related HTTP headers, mitigating common vulnerabilities like XSS and clickjacking. express-rate-limit will be implemented on all endpoints, with stricter limits on sensitive routes like login and registration, to prevent brute-force and denial-of-service attacks.
- Validation Middleware: A validation middleware using a library like Zod will be created. For every endpoint that accepts input, this middleware will validate the incoming request body, query parameters, and URL parameters against a predefined schema. This catches invalid data at the earliest possible stage.
- Centralized Error Handling: A final, catch-all error-handling middleware will be implemented. This ensures that any error thrown anywhere in the application (whether synchronously or asynchronously in route handlers) is caught. It will log the full error details for debugging purposes using a structured logger like Pino and send a generic, non-revealing 500-level error message to the client, preventing the leakage of sensitive stack traces.
Section 3.4: Database and ORM (PlanetScale & Prisma)
Database Setup and Schema
The project will use PlanetScale as its database provider. A new database will be created, and its main branch will be immediately protected by enabling “safe migrations”. This feature ensures that schema changes are applied without locking tables or causing downtime, a critical feature for a live application. All development and schema changes will occur on separate, isolated database branches.
Prisma Integration and Workflow
Prisma will serve as the ORM, providing a type-safe interface to the database.
- Initialization: The project will be set up with npx prisma init. The prisma/schema.prisma file will be configured with provider = “mysql” to match PlanetScale. Crucially, relationMode = “prisma” will be set in the datasource block. This tells Prisma to handle relationships at the application level rather than relying on foreign key constraints in the database, a requirement for PlanetScale’s scalable, sharded architecture.
- Connection Management: The database connection string will be stored exclusively in an environment variable named DATABASE_URL. This variable will be managed by the deployment platform’s secrets management system (e.g., Railway’s environment variables).
- Migration Workflow: The team will follow a strict, non-destructive migration workflow that aligns with PlanetScale’s branching model:
- A new database branch is created for the feature using the PlanetScale CLI: pscale branch create <db_name> <branch_name>.
- A secure, local proxy connection to this new branch is established: pscale connect <db_name> <branch_name>.
- The developer’s local .env file is updated to point DATABASE_URL to the local proxy address (e.g., mysql://root@127.0.0.1:3309/<db_name>).
- Schema modifications are made in the prisma/schema.prisma file.
- The command npx prisma db push is used to sync the local schema changes to the remote development branch in PlanetScale. The prisma migrate dev command, which generates migration files, is intentionally avoided because PlanetScale manages the schema deployment process itself.
- Once changes are verified, a “deploy request” is created in the PlanetScale UI or via CLI, which, when merged, applies the schema changes to the production main branch non-blockingly.
Section 3.5: Real-Time Analytics Sub-system
Architecture and Data Flow
The real-time analytics system will use a server-push model to deliver live statistics to a dedicated, authenticated admin dashboard. This architecture is efficient and scalable, as the server controls the data flow, and clients do not need to constantly poll for updates.
The data flow is as follows:
- The Node.js backend periodically (e.g., every 5 seconds) runs aggregation queries against the PlanetScale database to compute the latest statistics.
- The server then uses Socket.IO to broadcast these freshly computed stats to all connected admin clients.
- The frontend dashboard receives these updates via a WebSocket connection and dynamically updates its UI components (charts, counters, etc.) in real-time.
Backend Implementation (Socket.IO)
The standard Express server will be wrapped in a Node.js http server, which is then passed to the Socket.IO Server constructor. This allows both the Express API and the WebSocket server to run on the same port. A setInterval loop will be established within the server’s logic. This loop will contain the asynchronous functions that query the database for aggregated stats. Upon successful retrieval and calculation of new stats, the server will emit a global event to all connected sockets: io.emit(‘stats_update’, newStatsObject).
Frontend Implementation (Socket.IO Client)
The admin dashboard, upon loading, will initialize a connection to the Socket.IO server. It will then register a listener for the stats_update event. The callback function for this listener will receive the newStatsObject and be responsible for updating the state of the UI components, causing the charts and graphs to re-render with the latest data.
Analytics Database Schema
Standard analytics schemas often track generic events like session_start or purchase. However, to power the novel metrics required for ‘Flummox‽ing’, a more specialized schema is necessary. To calculate “Time-to-Epiphany,” the system must record the timestamp of a player’s first interaction with a puzzle and their first successful completion. To calculate “Rube Goldberg Efficiency,” the system must log the specific components used in a successful solution to compare against a predefined optimal solution. This leads to an event-sourced design with an events table for discrete player actions and an attempts table to aggregate these actions into distinct puzzle-solving sessions.
Table 3: Real-Time Game Analytics Database Schema (Prisma Syntax)
This schema provides the data foundation for understanding nuanced player behavior and validating the “positive failure” design loop. It captures the raw data needed to calculate our unique Key Performance Indicators (KPIs) and effectively balance the game.
Code snippet
// Located in prisma/schema.prisma
// Stores core player information
model Player {
id String @id @default(cuid())
username String @unique
email String @unique
createdAt DateTime @default(now())
lastLogin DateTime?
// Relations
events PlayerEvent
puzzleAttempts PuzzleAttempt
lifetimeStats PlayerLifetimeStats?
}
// Logs every significant action a player takes
model PlayerEvent {
id String @id @default(cuid())
playerId String
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
eventType String // e.g., ‘PUZZLE_START’, ‘PUZZLE_SUCCESS’, ‘COMPONENT_PLACE’, ‘RESET_LEVEL’
puzzleId String
eventData Json? // Flexible JSON field for event-specific data, e.g., { “component”: “gear_small”, “position”: { “x”: 100, “y”: 250 } }
timestamp DateTime @default(now())
@@index([playerId, timestamp])
}
// Tracks a player’s session for a specific puzzle
model PuzzleAttempt {
id String @id @default(cuid())
playerId String
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
puzzleId String
startTime DateTime
endTime DateTime? // Null until success or give-up
isSuccess Boolean @default(false)
componentCount Int // Number of components used in the final successful attempt
timeToEpiphanySecs Int? // Calculated on success: (endTime – startTime) in seconds
@@index([playerId, puzzleId])
}
// Stores aggregated lifetime statistics for a player for quick retrieval
model PlayerLifetimeStats {
id String @id @default(cuid())
playerId String @unique
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
totalPlaytimeSecs Int @default(0)
puzzlesSolved Int @default(0)
avgTimeToEpiphany Float @default(0.0)
avgRubeGoldbergEff Float @default(0.0) // Calculated as (Optimal Components / Actual Components)
}
Part 4: Asset Management Pipeline
This section outlines the systematic process for creating, optimizing, and integrating all visual and audio assets into the game. The pipeline is designed to maximize performance, especially initial load times, and to streamline the workflow for artists and developers.
Section 4.1: Asset Organization and Loading
Directory Structure
A strict and logical directory structure for assets is crucial for project scalability. All game assets, both in their raw and processed forms, will be stored within the public/assets/ directory, a practice recommended by the Vite project template as it ensures assets are directly copied to the build output without processing. This base directory will be further organized by asset type to maintain clarity:
- public/assets/images/: For individual images and source files for sprites.
- public/assets/audio/: For sound effects and music tracks.
- public/assets/atlases/: For the generated texture atlas .png and .json files.
- public/assets/fonts/: For any custom bitmap or web fonts.
Loading Strategy
To provide a fast and responsive user experience, asset loading will be strategically staged. A dedicated PreloaderScene will be implemented as the first scene that runs when the game starts. This scene’s preload method will be responsible for loading only the minimum assets required to display the main menu and a visual loading bar (e.g., a logo, background, and progress bar graphics).
All other game assets, which are specific to certain levels or game states, will be loaded within the preload method of their respective Scenes (e.g., LevelOneScene will load assets for level one). This “just-in-time” loading approach prevents a long, monolithic initial load time for the entire game, getting the player to the main menu as quickly as possible. Phaser’s global Cache Manager ensures that if an asset is loaded by one scene, it is immediately available to all other scenes without needing to be re-loaded from the network, optimizing memory and network usage.
Section 4.2: Texture Atlas Creation and Optimization
Rationale
The use of texture atlases is a non-negotiable optimization for high-performance web games. A texture atlas combines multiple smaller images (sprites) into a single large texture sheet. This technique provides two critical performance benefits:
- Reduced HTTP Requests: The browser only needs to download one image file instead of dozens or hundreds, dramatically speeding up the game’s initial load time.
- Optimized Rendering: The GPU can render multiple different sprites from the same texture sheet without needing to switch textures (a process known as a “texture bind”). Minimizing these texture swaps is one of the most effective ways to improve rendering performance and achieve a higher frame rate.
Tooling
TexturePacker will be the officially mandated tool for creating and managing texture atlases. Its selection is based on its robust, professional feature set and excellent, first-class support for the Phaser 3 framework, which includes essential features like pivot point editing, multi-pack atlases (splitting a large atlas across multiple files automatically), and normal map packing. For very small or non-critical atlases, the free web-based tool free-tex-packer.com can serve as a viable alternative.
Workflow
- Add Sprites to TexturePacker: Developers or artists will drag and drop individual sprite images or entire folders of sprites directly into the TexturePacker interface. Using folders is the recommended approach, as TexturePacker can be configured to use sub-folder names as part of the final sprite name, which helps to create a logical and collision-free naming scheme (e.g., a sprite at enemies/golem/walk_01.png becomes a frame named enemies/golem/walk_01.png).
- Configure Export Format: Within TexturePacker, the “Data Format” must be set to Phaser 3 (JSON Hash). This ensures the exported JSON file is perfectly compatible with Phaser’s atlas loader.
- Optimize for Production: For all production builds, two key optimizations will be applied:
- Texture Format: The format will be set to PNG-8 (indexed). This quantization technique can reduce final image file size by over 70% with very little perceptible loss in quality, which is a massive gain for web delivery performance.
- JSON Minification: The exported JSON data file will be minified to reduce its file size, further speeding up download times.
- Export Assets: TexturePacker will be configured to export the final .png texture sheet and the corresponding .json data file into the public/assets/atlases/ directory of the project.
- Load Atlas in Phaser: The generated atlas will be loaded in a Scene’s preload method using a single line of code: this.load.atlas(‘atlas-key’, ‘assets/atlases/my_atlas.png’, ‘assets/atlases/my_atlas.json’). The atlas-key is a unique identifier for this atlas in Phaser’s cache.
- Use Sprites in Game: Once loaded, any sprite from the atlas can be instantiated in the game by referencing the atlas key and the specific frame name: this.add.sprite(x, y, ‘atlas-key’, ‘frame-name.png’).
Part 5: Quality Assurance, Security, and Performance
This section defines the comprehensive strategy for ensuring the application is robust, secure, and performant. It integrates quality checks throughout the entire development lifecycle, from local pre-commit hooks to the final pre-deployment gates in the CI/CD pipeline.
Section 5.1: The Flummox‽ing Testing Pyramid
Strategy
The project’s testing strategy is built upon the well-established principles of the Test Pyramid, which advocates for a large base of fast, isolated unit tests, a smaller layer of more integrated tests, and a minimal number of slow, comprehensive end-to-end tests. This structure provides the optimal balance between test execution speed (fast feedback loop) and confidence in the overall system’s correctness.
A critical consideration in a full-stack TypeScript project using both Vitest and Cypress is the potential for global type conflicts. Both frameworks can attempt to define global test functions and assertion styles (e.g., expect). Cypress uses the Chai assertion library, while Vitest provides a Jest-compatible API. A naive setup can lead to TypeScript errors and developer confusion. To proactively prevent this, the testing strategy will enforce a clear separation: Vitest will be configured with globals: false, requiring explicit imports (import { describe, it, expect, vi } from ‘vitest’) in every unit and integration test file. Cypress, which is more invasive with its globals, will be allowed to “win” the global namespace, but its scope will be contained within its own cypress/ directory, which will have a separate tsconfig.json file to manage its types independently of the main application source. This approach prevents conflicts and ensures a smooth developer experience.
Layer 1: Unit Tests (Vitest)
- Scope: These tests target the smallest possible units of code in isolation. Examples include: a pure utility function for physics calculations, a single service function in the backend, or a stateless UI component in the admin dashboard. Dependencies are mocked extensively.
- Tooling: Vitest is the chosen framework for this layer. Its high speed, native support for modern ES Modules (ESM), and Jest-compatible API make it a perfect fit for the Vite-based frontend and the Node.js backend.
- Execution: Unit tests provide the fastest feedback. They will be configured to run on a pre-push Git hook to catch errors before code even leaves a developer’s machine. They also run as the very first stage in the CI pipeline on every commit. Individual test execution time should be under 100ms.
Layer 2: Integration Tests (Vitest + Supertest)
- Scope: These tests verify the interaction between multiple units or components. On the backend, this involves testing an API controller to ensure it correctly calls its dependent services and interacts with a mocked database layer. On the frontend, this could involve testing that a Phaser Scene correctly initializes and manages its child components.
- Tooling: For the backend, Vitest will be combined with the supertest library. This allows for making in-memory HTTP requests to the Express API endpoints, testing the entire request-response cycle (including routing and middleware) without the overhead of a running network server.
- Execution: Integration tests are run as a second stage in the CI pipeline, after the unit tests have passed.
Layer 3: End-to-End (E2E) Tests (Cypress)
- Scope: E2E tests simulate a real user’s journey through the application in a live browser environment. They are reserved for testing critical user flows. Examples include: “A player can successfully log in, navigate to the first puzzle, place three components, run the simulation, and see their solution fail correctly.” or “An administrator can log into the admin dashboard, view the real-time analytics page, and verify that the user count updates when a new user connects.”
- Tooling: Cypress is the designated tool for this layer due to its superior developer experience, reliable automatic waiting for elements, and powerful debugging features like time-traveling through commands.
- Execution: E2E tests are the slowest and most resource-intensive. They will be run sparingly in the CI pipeline, triggered only on pull requests targeting the main branch and as a final quality gate before a production deployment. To ensure test stability and isolate the frontend, all backend API calls will be stubbed using Cypress’s cy.intercept() command, providing predictable data and preventing test failures due to backend issues.
Section 5.2: Security Hardening Checklist
This checklist provides an actionable, auditable list of security measures to be implemented across the full stack. It combines best practices from multiple authoritative sources.
Backend (Node.js/Express/MySQL)
- Input Validation: All data originating from the client (request body, URL parameters, query strings, headers) is considered untrusted and must be rigorously validated against a strict schema using a library like Zod. This is the first line of defense against a wide range of injection attacks.
- Authentication & Authorization: Authentication will be implemented using JSON Web Tokens (JWT). Passwords will never be stored in plaintext; they will be hashed using a strong, salted, and slow hashing algorithm like bcrypt. Once authenticated, authorization will be enforced to ensure users can only access the resources they are permitted to.
- Secure Session Management: Session tokens will be configured with security flags (HttpOnly, Secure, SameSite=Strict) and will have appropriate expiration timeouts to minimize the window for session hijacking.
- Rate Limiting: Global rate limiting will be applied to all API endpoints, with stricter, more aggressive limits placed on authentication routes (/login, /register) to mitigate brute-force attacks.
- HTTP Security Headers: The helmet middleware will be used to automatically set a suite of security-related HTTP headers (e.g., Content-Security-Policy, X-Content-Type-Options, Strict-Transport-Security), which helps prevent common web vulnerabilities.
- Dependency Vulnerability Scanning: The CI pipeline will include a step that runs npm audit or integrates a tool like Snyk to scan all project dependencies for known vulnerabilities. The build will fail if high-severity vulnerabilities are found.
- Secure Error Handling: The centralized error handler will ensure that no internal error details, such as stack traces or database error messages, are ever leaked to the client in production responses.
- ORM Security: All database interactions will be performed through the Prisma client. Its generated, type-safe query methods inherently prevent SQL injection vulnerabilities. Raw SQL queries ($queryRaw) will be avoided unless absolutely necessary, and if used, will never incorporate un-sanitized user input.
- Secrets Management: All sensitive configuration values (API keys, database connection strings, JWT secrets) will be managed exclusively through environment variables. These will never be hardcoded into the source code. The deployment platforms (Railway, Vercel) and GitHub Actions provide secure mechanisms for managing these secrets.
Frontend (Phaser/Vite)
- Secure localStorage: Sensitive data, particularly authentication tokens, must not be stored in localStorage as plain text, as it is vulnerable to XSS attacks. If client-side storage is required for tokens, they will be encrypted using a library like crypto-js. The encryption key will be held in memory for the duration of the session and will not be hardcoded.
- Cross-Site Scripting (XSS) Prevention: Any user-generated content that is displayed in the UI (e.g., usernames on a leaderboard) will be properly sanitized to neutralize any embedded scripts.
- HTTPS Enforcement: The entire application will be served exclusively over HTTPS, which will be enforced by the hosting platforms (Vercel and Railway).
Section 5.3: Performance Optimization Checklist
This checklist outlines proactive measures for monitoring and optimizing application performance.
Backend
- Structured Logging: A high-performance, structured logger like Pino will be used instead of console.log. Structured logging (e.g., in JSON format) is significantly more efficient to parse and analyze, and has lower overhead in a production environment.
- Database Query Optimization: The backend will include tooling to log and analyze slow database queries. Prisma’s logging capabilities can be configured to output query performance, helping to identify and index bottlenecks.
- Event Loop Monitoring: The application will be monitored for event loop blockage. A blocked event loop is a sign of long-running synchronous code that freezes the entire Node.js process, severely degrading performance. Tools like event-loop-lag can be used for this purpose.
- Load Testing: Before major releases, automated load tests will be run against a staging environment to understand how the application performs under high concurrency and to identify potential scaling bottlenecks.
Frontend
- Bundle Size Analysis: The Vite build process will be augmented with the vite-plugin-bundle-visualizer plugin. This generates an interactive treemap of the final production bundle, allowing developers to identify large dependencies that could be replaced or code-split.
- Asset Optimization: As detailed in the Asset Management Pipeline (Part 4), all image assets will be compressed, and texture atlases will be used to reduce draw calls and improve rendering performance.
- Code Splitting: Vite’s native support for dynamic import() statements will be used to implement code splitting. This allows for lazy-loading certain parts of the application, such as specific Phaser Scenes or heavy UI components, so they are only downloaded by the browser when they are actually needed.
- Browser Caching: The deployment platform (Vercel) will be configured to serve assets with optimal Cache-Control headers, instructing the browser to cache assets aggressively and reducing load times for repeat visitors.
Part 6: CI/CD and Deployment
This section details the automated pipeline that will build, test, secure, and deploy the application. The Continuous Integration/Continuous Deployment (CI/CD) pipeline is the backbone of the project’s release process, designed to ensure fast, reliable, and safe delivery of new features and fixes.
Section 6.1: GitHub Actions Workflow for a Monorepo
Monorepo Strategy and Workflow
The project will be structured as a monorepo, with the client-side and server-side codebases co-located in a single Git repository under the packages/ directory: packages/client for the Vite/Phaser frontend and packages/server for the Node.js/Express backend.
A single, comprehensive GitHub Actions workflow file, main.yml, will be created in the .github/workflows/ directory to manage the entire CI/CD process.
A critical optimization for efficiency in a monorepo is to avoid running irrelevant checks. A naive CI setup would run all frontend and backend tests for every commit, which is wasteful. To solve this, the workflow will leverage the on.<push|pull_request>.paths filter in its trigger configuration. Separate jobs will be defined for the client and server, and each job will be conditionally executed based on which part of the codebase was modified. For example, the server-test job will only run if changes are detected in files under the packages/server/** path. This path-based filtering ensures that only the necessary checks are performed, dramatically speeding up the feedback loop for developers and reducing CI resource consumption.
Workflow Triggers
The workflow will be configured to trigger automatically on two primary events:
- On every push to the main branch, which will initiate the full test, build, and deploy sequence.
- On every pull_request opened or updated that targets the main branch, which will run all tests and quality checks as a gate before merging is allowed.
Section 6.2: Automated Security and Performance Checks in CI
The CI pipeline will serve as an automated guardian of code quality, security, and performance.
Security Scanning
- Static Application Security Testing (SAST): On every pull request, a dedicated job will use a tool like GitHub’s native CodeQL or an integrated service like Snyk to perform a deep static analysis of the source code, identifying potential security vulnerabilities such as injection flaws or insecure code patterns.
- Dependency Scanning: A job will execute npm audit –audit-level=high. This command scans all installed dependencies against the npm vulnerability database and will fail the CI build if any high or critical severity vulnerabilities are found, forcing an update before the code can be merged.
- Secrets Scanning: An additional scanning step using a tool like TruffleHog will be integrated to scan commits for any accidentally hardcoded secrets (e.g., API keys, passwords). This acts as a final safety net to prevent credentials from being exposed in the Git history.
Performance Checks
- Test Execution Time: Each test job in the pipeline will have a timeout-minutes property. If the tests take longer than a predefined threshold, the job will fail. This helps to catch performance regressions within the test suite itself.
- Frontend Bundle Size: The frontend build job will include a step that calculates the size of the final production JavaScript bundle. This size will be checked against a baseline, and if a PR causes a significant increase, a comment will be automatically posted to the PR, alerting the reviewer to a potential performance impact.
Section 6.3: Deployment Strategy
Continuous Deployment Model
The project will follow a Continuous Deployment model, where any commit merged into the main branch that passes all CI checks is automatically deployed to the production environment.
Backend Deployment to Railway
- Containerization: A Dockerfile will be maintained in the packages/server directory. This file will define the steps to create a lightweight, production-ready container image for the Node.js application.
- Configuration & Secrets: All environment-specific configurations, including the DATABASE_URL from PlanetScale and other API secrets, will be securely stored in the Railway project’s environment variables dashboard. The application is designed to read these at runtime.3
- Deployment Job: The GitHub Actions workflow will contain a deploy-server job that runs only on merges to main. This job will use the official Railway CLI (railway up) to trigger a new deployment on the Railway platform, which will pull the latest code, build the Docker image, and deploy it.
Frontend Deployment to Vercel
- Configuration: The Vercel project will be directly linked to the project’s GitHub repository. In the Vercel dashboard, the “Framework Preset” will be set to “Vite,” which automatically configures the correct build commands (npm run build) and output directory (dist).
- Secrets: Any frontend-specific environment variables, such as the public URL of the backend API (VITE_API_URL), will be configured within the Vercel project’s settings.
- Automated Deployment: Vercel’s deep integration with GitHub automates the deployment process. When a commit is pushed to the main branch, Vercel is notified via a webhook. It automatically pulls the latest code, executes the build command, and deploys the resulting static assets from the dist directory to its global edge network. This process requires no explicit deployment job in the GitHub Actions workflow file.
Table 4: CI/CD Pipeline Stages and Jobs (.github/workflows/main.yml)
This table serves as a high-level blueprint for the actual YAML workflow file. It clearly defines the jobs, their dependencies, and the key steps involved, making the pipeline’s logic easy to understand and implement.
Job Name | Runs On | Triggered By (Path Filter) | Depends On | Key Steps |
server-test | ubuntu-latest | Changes in packages/server/** | – | actions/setup-node, npm ci, npm run test:unit (Vitest), npm run test:integration (Vitest+Supertest) |
server-scan | ubuntu-latest | Changes in packages/server/** | server-test | npm audit, snyk/actions/node, Secrets Scan |
server-build | ubuntu-latest | Changes in packages/server/** | server-scan | docker/build-push-action (Builds and pushes Docker image to a registry) |
deploy-server | ubuntu-latest | Push to main & changes in packages/server/** | server-build | railway up –service=<service_id> (Deploys to Railway) |
client-test | ubuntu-latest | Changes in packages/client/** | – | actions/setup-node, npm ci, npm run test:unit (Vitest) |
client-e2e-test | ubuntu-latest | PR to main & changes in packages/client/** | client-test | cypress-io/github-action (Runs E2E tests against a preview deployment) |
client-scan | ubuntu-latest | Changes in packages/client/** | client-test | npm audit, snyk/actions/node, Bundle Size Check |
client-build | ubuntu-latest | Changes in packages/client/** | client-scan | npm run build (Vite build step for Vercel) |
deploy-client | ubuntu-latest | Push to main & changes in packages/client/** | client-build | No explicit step needed. Vercel’s GitHub integration handles deployment automatically upon successful completion of checks. |
Conclusion
This project plan outlines a comprehensive and modern approach to developing ‘Flummox‽ing,’ a web-based 2.5D physics puzzler. By integrating advanced technologies and methodologies from the outset, the project is positioned for both creative success and technical robustness.
The foundational strategy, centered on the principle of “Positive Failure,” directly informs the technical architecture, necessitating a real-time analytics backend to measure and refine the player experience. This ensures that game design goals are not just abstract concepts but are supported by concrete data and engineering decisions.
The proposed AI-assisted development workflow, #ai-draft -> #human-review, represents a forward-thinking methodology. It pragmatically leverages the speed of AI for code generation while enforcing strict human oversight and accountability through a structured Git and pull request process. The AI Prompt Codex and automated AI code reviews are designed to standardize quality and offload repetitive tasks, freeing human developers to focus on complex problem-solving and architectural integrity.
The full-stack technical specifications define a cohesive and performant application using a modern, type-safe TypeScript ecosystem. The selection of Phaser 3 with Vite and Rapier.js for the frontend, and Node.js with Express, Prisma, and PlanetScale for the backend, provides a powerful and scalable foundation. The detailed asset management pipeline, centered on TexturePacker, ensures that the game will be optimized for web delivery.
Finally, the multi-layered quality assurance strategy, based on the testing pyramid, combines the strengths of Vitest for unit/integration testing and Cypress for end-to-end validation. This is embedded within a robust CI/CD pipeline on GitHub Actions, which includes automated security scanning, performance checks, and a decoupled deployment strategy to Vercel and Railway. This comprehensive plan serves as a definitive blueprint for building ‘Flummox‽ing’ to the highest standards of quality, security, and performance.
Works cited
- r/outerwilds Wiki: Games Like Outer Wilds – Reddit, accessed June 18, 2025, https://www.reddit.com/r/outerwilds/wiki/index/gamerecs/
- phaserjs/rapier-connector: Easily use the Rapier physics … – GitHub, accessed June 18, 2025, https://github.com/phaserjs/rapier-connector
- accessed December 31, 1969, https://docs.railway.app/guides/nodejs
The Flummox Project Genesis Kit
This document serves as the complete technical scaffolding and implementation blueprint for the Flummox project. It is designed to be ingested by a development team and a partner LLM (such as Gemini Pro 2.5) to bootstrap the entire application foundation, from repository structure to deployment-ready CI/CD pipelines. Every component herein is architecturally cohesive and adheres to production-grade standards.
Section 1: Project Foundation: Repository and Environment
This section establishes the canonical source of truth for the project’s structure and local development environment. A well-defined monorepo is the lynchpin of the entire development lifecycle, ensuring consistency across services, simplifying dependency management, and enabling seamless integration between front-end, back-end, and testing suites. The adoption of a monorepo architecture managed by pnpm workspaces is a strategic decision that fosters a unified approach to tooling, linting, and dependency management. This structure directly enables the seamless cross-referencing required by other deliverables; for example, Cypress E2E tests in the apps/e2e package can easily import types from the packages/shared-types package, which are also consumed by the apps/server and apps/client applications. This eliminates the complexities of managing separate repositories and their inter-dependencies.
1.1. Monorepo Directory Architecture
The following directory tree represents the canonical structure for the Flummox project. This structure is designed for scalability and clarity, using the standard apps and packages convention to delineate between deployable applications and shared, reusable code.
Plaintext
flummox-monorepo/
├──.github/
│ └── workflows/
│ └── ci.yml # Main CI/CD pipeline for testing and merge gating
├──.gitignore
├──.prettierrc.json # Code formatting rules
├── apps/
│ ├── client/ # React/Phaser front-end application
│ │ ├── public/
│ │ ├── src/
│ │ │ ├── assets/ # Game assets (images, audio, puzzle data)
│ │ │ ├── components/ # Reusable React UI components (Button, Card, etc.)
│ │ │ ├── scenes/ # Phaser 3 scenes (PuzzleScene.ts)
│ │ │ ├── services/ # Client-side services (API client, Socket.io client)
│ │ │ ├── styles/ # Global styles and design tokens
│ │ │ ├── App.tsx
│ │ │ └── main.tsx
│ │ └── package.json
│ ├── e2e/ # Cypress end-to-end testing suite
│ │ ├── cypress/
│ │ │ ├── e2e/ # E2E test specifications (*.cy.ts)
│ │ │ └── support/
│ │ ├── cypress.config.ts
│ │ └── package.json
│ └── server/ # Node.js/Express back-end application
│ ├── src/
│ │ ├── api/ # API route definitions
│ │ ├── config/ # Server configuration (database, CORS, etc.)
│ │ ├── services/ # Business logic (auth, analytics, etc.)
│ │ ├── sockets/ # Socket.io event handlers
│ │ └── index.ts # Server entry point
│ └── package.json
├── config/
│ ├── eslint-preset.js # Shared ESLint configuration
│ └── tsconfig.base.json # Shared TypeScript base configuration
├── packages/
│ ├── shared-types/ # Shared TypeScript types and interfaces (e.g., Puzzle, User)
│ │ ├── src/
│ │ └── package.json
│ └── ui-core/ # Shared React component library (future use)
│ ├── src/
│ └── package.json
├──.env.example # Template for environment variables
├── docker-compose.yml # Docker configuration for local services (e.g., MySQL)
├── package.json # Root package.json with workspace definitions and root scripts
└── pnpm-workspace.yaml # pnpm workspace configuration
Directory Annotations:
- apps/: Contains independently deployable applications.
- client: The user-facing web application built with React and Phaser.
- server: The Node.js back-end API and WebSocket server.
- e2e: The Cypress test runner application, which tests the client and server in a production-like environment.
- packages/: Contains shared code, types, and utilities that are not deployable on their own but are consumed by the applications in the apps/ directory.
- shared-types: A critical package for ensuring type safety between the client and server. It defines common data structures like puzzle definitions and API payloads.
- ui-core: A placeholder for a future shared component library, promoting UI consistency.
- .github/: Houses all CI/CD workflow configurations for GitHub Actions.
- config/: Centralizes configuration for development tools like ESLint and TypeScript, enforcing project-wide standards.
1.2. Master README.md
This file serves as the primary entry point for any developer joining the project. It provides all necessary information to set up and run the application locally.
Flummox: An AI-Integrated Full-Stack Web Puzzler
Welcome to the Flummox project repository. This monorepo contains the complete source code for the Flummox web application, including the front-end client, back-end server, and all related testing and development packages.
Project Overview
Flummox is a 2D physics-based puzzle game designed for modern web browsers. It features a dynamically loaded puzzle system, real-time analytics, and an AI-augmented development workflow.
Technology Stack
- Frontend: React, Phaser 3, TypeScript, Socket.io Client
- Backend: Node.js, Express, TypeScript, Socket.io, MySQL
- Database: MySQL 8
- Testing: Jest, Cypress
- DevOps: Docker, GitHub Actions, pnpm Workspaces
Prerequisites
Before you begin, ensure you have the following tools installed on your system:
- Node.js (v18 or later)
- pnpm (v8 or later)
- (https://www.docker.com/get-started/) and Docker Compose
Local Development Setup
Follow these steps to get the entire Flummox application running on your local machine.
1. Clone the Repositorybash
git clone https://github.com/your-org/flummox-monorepo.git
cd flummox-monorepo
### 2. Install Dependencies
This project uses `pnpm` workspaces. A single command at the root will install all dependencies for all applications and packages within the monorepo.
“`bash
pnpm install
3. Configure Environment Variables
Copy the example environment file and populate it with your local configuration details.
Bash
cp.env.example.env
Open the newly created .env file and fill in the required values, such as database credentials and a JWT secret.
4. Start Local Services
The project requires a MySQL database. The provided docker-compose.yml file makes it easy to spin up a containerized instance.
Bash
docker-compose up -d
This will start a MySQL server in the background.
5. Run the Application
A single command will concurrently start the React client and the Node.js server in development mode with hot-reloading enabled.
Bash
pnpm dev
- The React client will be available at http://localhost:5173.
- The Node.js server will be running at http://localhost:3000.
Key Scripts
The following scripts can be run from the root of the monorepo:
Script | Description |
pnpm dev | Starts the client and server in development mode. |
pnpm build | Builds all applications for production. |
pnpm test | Runs all Jest unit and integration tests across the monorepo. |
pnpm test:e2e | Runs the full Cypress end-to-end testing suite (requires a running app). |
pnpm lint | Lints and formats all code according to project standards. |
pnpm db:migrate | Applies database schema migrations (using a tool like knex). |
AI-Augmented Development Workflow
This project encourages the use of generative AI to accelerate development. To maintain code quality and accountability, we follow a strict contribution protocol.
- Prompt Cookbook: Refer to the PROMPT_COOKBOOK.md file at the root of this project for a set of reusable, high-quality prompts for common development tasks.
- Commit Protocol:
- #ai-draft: Use this tag in the first commit message for any code substantially generated by an AI. This marks the code as a first draft requiring human verification.
- #rob-revise: After a developer has thoroughly reviewed, tested, refactored, and taken full ownership of the AI-generated code, they must make a subsequent commit using this tag.
- Pull Request Policy: A pull request is only considered ready for merge when all #ai-draft commits have a corresponding #rob-revise commit, and the developer adds the #human-review label to the pull request. This label is required to pass the CI checks.
## Section 2: Core Gameplay Implementation
This section delivers the initial, playable assets for the game engine. These assets establish a strict data contract between the game content, defined in JSON, and the game engine, implemented in Phaser. This pattern is foundational for all future puzzle development, ensuring that content creation is decoupled from engine programming.
### 2.1. Seed Puzzle Definitions (JSON)
The following two JSON files represent the first puzzles for Flummox. Their schemas are self-documenting and demonstrate a range of core mechanics. These files should be placed in `apps/client/src/assets/puzzles/`.
#### Puzzle 1: `p001_aligned_gateway.json`
This puzzle introduces basic physics objects, static obstacles, and a simple win condition: moving a specific block into a target zone.
“`json
{
“puzzleId”: “p001_aligned_gateway”,
“name”: “The Aligned Gateway”,
“difficulty”: 1,
“world”: {
“gravity”: { “x”: 0, “y”: 1 },
“bounds”: { “width”: 800, “height”: 600 }
},
“entities”:,
“winConditions”:
}
Puzzle 2: p002_resonant_crystal.json
This puzzle introduces a more complex interaction: a “tap/hold” mechanic to charge a crystal, which then emits a force to move another object. This provides a direct test case for the mobile controls specified in Section 3.1.
JSON
{
“puzzleId”: “p002_resonant_crystal”,
“name”: “The Resonant Crystal”,
“difficulty”: 3,
“world”: {
“gravity”: { “x”: 0, “y”: 0.5 },
“bounds”: { “width”: 800, “height”: 600 }
},
“entities”:,
“winConditions”:
}
2.2. Phaser 3 Puzzle Loader Scene (TypeScript)
This minimal but complete Phaser scene, PuzzleScene.ts, demonstrates how to load, verify, and instantiate the puzzle entities defined in the JSON files. It acts as the critical bridge between data and gameplay. This file should be placed in apps/client/src/scenes/.
TypeScript
// apps/client/src/scenes/PuzzleScene.ts
import Phaser from ‘phaser’;
// Assuming types are imported from the shared package
import { PuzzleDefinition, PuzzleEntity } from ‘@flummox/shared-types’;
export class PuzzleScene extends Phaser.Scene {
private puzzleData!: PuzzleDefinition;
private gameObjects: Map<string, Phaser.GameObjects.GameObject> = new Map();
constructor() {
super({ key: ‘PuzzleScene’ });
}
init(data: { puzzleId: string }) {
// In a real app, you’d fetch this from a server or asset pack
// For this example, we’ll preload it.
}
preload() {
// In a real app, you would have a loading scene and load all assets.
// For this example, we assume assets are loaded and we load the puzzle definition.
this.load.json(‘p001’, ‘assets/puzzles/p001_aligned_gateway.json’);
this.load.json(‘p002’, ‘assets/puzzles/p002_resonant_crystal.json’);
// Example asset loading
this.load.image(‘block_blue’, ‘assets/images/block_blue.png’);
this.load.image(‘zone_green’, ‘assets/images/zone_green.png’);
}
create(data: { puzzleId: string }) {
this.puzzleData = this.cache.json.get(data.puzzleId);
if (!this.puzzleData) {
console.error(`Puzzle data for ${data.puzzleId} not found!`);
this.scene.start(‘MainMenuScene’); // Or some error scene
return;
}
this.setupPhysics();
this.createEntities();
this.setupWinConditionChecker();
}
private setupPhysics() {
const { gravity } = this.puzzleData.world;
this.matter.world.setGravity(gravity.x, gravity.y);
}
private createEntities() {
this.puzzleData.entities.forEach(entityData => {
let gameObject: Phaser.GameObjects.GameObject | null = null;
switch (entityData.type) {
case ‘DynamicBox’:
gameObject = this.matter.add.image(
entityData.position.x,
entityData.position.y,
entityData.assetKey
);
(gameObject.body as MatterJS.BodyType).restitution = entityData.physicsProperties.restitution |
| 0;
break;
case ‘StaticBox’:
gameObject = this.matter.add.image(
entityData.position.x,
entityData.position.y,
entityData.assetKey,
undefined,
{ isStatic: true }
);
break;
case ‘SensorZone’:
gameObject = this.matter.add.rectangle(
entityData.position.x,
entityData.position.y,
entityData.size.width,
entityData.size.height,
{ isSensor: true, isStatic: true }
);
// In a real implementation, you might add a visual sprite as well
break;
//… other entity types like InteractiveCharger would be handled here
default:
console.warn(`Unknown entity type: ${entityData.type}`);
break;
}
if (gameObject) {
this.gameObjects.set(entityData.id, gameObject);
}
});
}
private setupWinConditionChecker() {
this.matter.world.on(‘collisionstart’, (event: Phaser.Physics.Matter.Events.CollisionStartEvent) => {
for (const condition of this.puzzleData.winConditions) {
if (condition.type === ‘Overlap’) {
const entityA = this.getGameObjectByBody(event.pairs.bodyA);
const entityB = this.getGameObjectByBody(event.pairs.bodyB);
const entityAId = this.getIdByGameObject(entityA);
const entityBId = this.getIdByGameObject(entityB);
if (
(entityAId === condition.entityAId && entityBId === condition.entityBId) ||
(entityAId === condition.entityBId && entityBId === condition.entityAId)
) {
this.handleWin();
}
}
}
});
}
// Helper methods to map Matter bodies back to our entity IDs
private getGameObjectByBody(body: MatterJS.BodyType): Phaser.GameObjects.GameObject | undefined {
for (const [_, go] of this.gameObjects.entries()) {
if ((go as any).body === body) {
return go;
}
}
return undefined;
}
private getIdByGameObject(gameObject: Phaser.GameObjects.GameObject | undefined): string | undefined {
if (!gameObject) return undefined;
for (const [id, go] of this.gameObjects.entries()) {
if (go === gameObject) {
return id;
}
}
return undefined;
}
private handleWin() {
console.log(‘PUZZLE SOLVED!’);
// Trigger win UI, send analytics event, etc.
this.matter.world.pause();
this.add.text(400, 300, ‘You Win!’, { fontSize: ’64px’, color: ‘#00ff00’ }).setOrigin(0.5);
}
update(time: number, delta: number) {
// Main game loop for continuous checks if needed
}
}
Section 3: Player Experience and Design System
This section defines the user-facing aspects of the application: how the player interacts with the game world and the visual language that defines the Flummox brand. A systematic approach to both controls and visual design is essential for a polished and intuitive user experience.
3.1. Mobile-First Player Controls (PlayerController.ts)
The following PlayerController.ts file provides a robust control scheme for mobile and desktop platforms. It is designed as a controller class that can be instantiated and attached to a player-controlled GameObject in a Phaser scene. It implements both a virtual thumbstick for continuous movement and tap/hold logic for discrete interactions, directly supporting the mechanics required by puzzles like “The Resonant Crystal.” This file should be placed in apps/client/src/components/.
TypeScript
// apps/client/src/components/PlayerController.ts
import Phaser from ‘phaser’;
interface PlayerControllerConfig {
scene: Phaser.Scene;
playerObject: Phaser.Physics.Matter.Sprite;
speed: number;
}
export class PlayerController {
private scene: Phaser.Scene;
private playerObject: Phaser.Physics.Matter.Sprite;
private speed: number;
private cursors: Phaser.Types.Input.Keyboard.CursorKeys;
private virtualStick?: any; // Using ‘any’ for plugin compatibility, e.g., rex-plugins
constructor(config: PlayerControllerConfig) {
this.scene = config.scene;
this.playerObject = config.playerObject;
this.speed = config.speed;
// Keyboard controls as a primary input
this.cursors = this.scene.input.keyboard.createCursorKeys();
// Virtual thumbstick for touch devices
// This assumes a plugin like ‘rex-plugins/virtualjoystick’ is installed and configured
// Example: this.virtualStick = this.scene.plugins.get(‘rexVirtualJoystick’).add(…)
// For this example, we’ll simulate the logic.
this.setupPointerEvents();
}
private setupPointerEvents() {
let holdTimer: number;
let isHolding = false;
this.scene.input.on(‘pointerdown’, (pointer: Phaser.Input.Pointer) => {
isHolding = true;
holdTimer = this.scene.time.now;
// Emit a “tap” event immediately for responsive feedback
this.playerObject.emit(‘interaction:tap’, { x: pointer.worldX, y: pointer.worldY });
});
this.scene.input.on(‘pointerup’, (pointer: Phaser.Input.Pointer) => {
if (isHolding) {
const duration = this.scene.time.now – holdTimer;
if (duration > 200) { // Threshold for a “hold” in ms
this.playerObject.emit(‘interaction:hold_release’, { duration });
}
}
isHolding = false;
});
}
public update() {
const velocity = new Phaser.Math.Vector2(0, 0);
// Keyboard input
if (this.cursors.left.isDown) {
velocity.x = -1;
} else if (this.cursors.right.isDown) {
velocity.x = 1;
}
if (this.cursors.up.isDown) {
velocity.y = -1;
} else if (this.cursors.down.isDown) {
velocity.y = 1;
}
// Virtual stick input (conceptual)
if (this.virtualStick && this.virtualStick.force > 0) {
velocity.x = this.virtualStick.x;
velocity.y = this.virtualStick.y;
}
velocity.normalize().scale(this.speed);
this.playerObject.setVelocity(velocity.x, velocity.y);
}
}
/*******************************************************
* ACCESSIBILITY NOTES
*******************************************************
*
* 1. Keyboard Fallbacks:
* The current implementation uses arrow keys for movement. This is a solid foundation.
* For tap/hold actions, the spacebar should be mapped as an alternative.
* Example:
* this.scene.input.keyboard.on(‘keydown-SPACE’, () => {
* // Logic to start hold timer, similar to ‘pointerdown’
* });
* this.scene.input.keyboard.on(‘keyup-SPACE’, () => {
* // Logic to end hold timer and fire event, similar to ‘pointerup’
* });
*
* 2. Focus Management:
* The Phaser game canvas element in the DOM should have `tabindex=”0″` to be focusable.
* When the game starts or a puzzle loads, programmatically call `.focus()` on the canvas element
* to ensure keyboard events are captured without requiring a click first.
*
* 3. ARIA Attributes for Surrounding UI:
* While ARIA is not directly applicable to rendered objects within the canvas, all interactive
* HTML elements surrounding the game (e.g., “Reset Puzzle”, “Next Level” buttons) must
* be fully accessible.
* Example in a React component:
* <button aria-label=”Reset the current puzzle”>Reset</button>
*
* 4. Configurable Controls:
* In a full implementation, provide a settings menu where users can remap keys to their
* preference, enhancing accessibility for users with different needs or hardware.
*******************************************************/
3.2. Brand and Visual Style Guide
A robust design system is built on a foundation of design tokens. By defining colors, typography, and spacing as semantic variables, we decouple abstract design decisions from their specific implementation. This approach ensures visual consistency across the entire application and simplifies future updates or theming (e.g., implementing a “dark mode” becomes a matter of redefining the root token values).
3.2.1. Design Tokens (styles/tokens.css)
This file defines the entire visual language of Flummox as a set of CSS custom properties (variables). It should be imported globally in the main application stylesheet. This file should be placed in apps/client/src/styles/.
CSS
/* apps/client/src/styles/tokens.css */
:root {
/* ======================================== */
/* COLOR PALETTE */
/* ======================================== */
/* Brand Colors */
–color-brand-primary: #20D4A7; /* Bright Teal */
–color-brand-secondary: #5F27CD; /* Deep Purple */
–color-brand-accent: #F9A826; /* Warm Amber */
/* Interaction Colors */
–color-action-primary: var(–color-brand-primary);
–color-action-primary-hover: #1AB48D;
–color-action-secondary: var(–color-brand-secondary);
–color-action-secondary-hover: #4B1EAD;
/* Feedback Colors */
–color-feedback-success: #2ECC71; /* Green */
–color-feedback-error: #E74C3C; /* Red */
–color-feedback-warning: #F1C40F; /* Yellow */
–color-feedback-info: #3498DB; /* Blue */
/* Neutral/UI Colors */
–color-neutral-background: #F4F7F9;
–color-neutral-surface: #FFFFFF;
–color-neutral-border: #E1E8ED;
–color-neutral-text-strong: #14171A;
–color-neutral-text-default: #4A4A4A;
–color-neutral-text-subtle: #8899A6;
/* ======================================== */
/* TYPOGRAPHY */
/* ======================================== */
/* Font Families */
–font-family-sans: ‘Inter’, -apple-system, BlinkMacSystemFont, ‘Segoe UI’, Roboto, Helvetica, Arial, sans-serif;
–font-family-mono: ‘Fira Code’, ‘Courier New’, monospace;
/* Font Weights */
–font-weight-regular: 400;
–font-weight-medium: 500;
–font-weight-bold: 700;
/* Font Sizes (Modular Scale) */
–font-size-xs: 0.75rem; /* 12px */
–font-size-sm: 0.875rem; /* 14px */
–font-size-base: 1rem; /* 16px */
–font-size-md: 1.125rem; /* 18px */
–font-size-lg: 1.5rem; /* 24px */
–font-size-xl: 2.25rem; /* 36px */
–font-size-xxl: 3rem; /* 48px */
/* Line Heights */
–line-height-tight: 1.25;
–line-height-normal: 1.5;
–line-height-loose: 1.75;
/* ======================================== */
/* SPACING & LAYOUT */
/* ======================================== */
–spacing-unit: 4px;
–spacing-1: var(–spacing-unit); /* 4px */
–spacing-2: calc(var(–spacing-unit) * 2); /* 8px */
–spacing-3: calc(var(–spacing-unit) * 3); /* 12px */
–spacing-4: calc(var(–spacing-unit) * 4); /* 16px */
–spacing-6: calc(var(–spacing-unit) * 6); /* 24px */
–spacing-8: calc(var(–spacing-unit) * 8); /* 32px */
/* ======================================== */
/* BORDERS & RADII */
/* ======================================== */
–border-radius-sm: 4px;
–border-radius-md: 8px;
–border-radius-lg: 16px;
–border-radius-full: 9999px;
–border-width-sm: 1px;
–border-width-md: 2px;
}
3.2.2. React Component Style Reference
These example React components demonstrate how to consume the design tokens to build a consistent and maintainable UI. They use CSS Modules for scoped styling, a common and effective pattern in modern React applications. These files would reside in apps/client/src/components/.
Button.tsx
TypeScript
// apps/client/src/components/Button.tsx
import React from ‘react’;
import styles from ‘./Button.module.css’;
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ‘primary’ | ‘secondary’;
children: React.ReactNode;
}
export const Button: React.FC<ButtonProps> = ({
variant = ‘primary’,
children,
…props
}) => {
const buttonClass = `${styles.button} ${styles[variant]}`;
return (
<button className={buttonClass} {…props}>
{children}
</button>
);
};
Button.module.css
CSS
/* apps/client/src/components/Button.module.css */
.button {
padding: var(–spacing-3) var(–spacing-6);
border-radius: var(–border-radius-md);
font-family: var(–font-family-sans);
font-size: var(–font-size-base);
font-weight: var(–font-weight-medium);
border: none;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
display: inline-flex;
align-items: center;
justify-content: center;
}
.button:disabled {
cursor: not-allowed;
opacity: 0.6;
}
.primary {
background-color: var(–color-action-primary);
color: var(–color-neutral-surface);
}
.primary:hover:not(:disabled) {
background-color: var(–color-action-primary-hover);
}
.secondary {
background-color: var(–color-action-secondary);
color: var(–color-neutral-surface);
}
.secondary:hover:not(:disabled) {
background-color: var(–color-action-secondary-hover);
}
Table: Typography System Reference
This table provides a clear guide for developers and designers on how to apply the typography tokens, preventing misuse and ensuring a consistent typographic hierarchy.
Token Name | Intended Use | Font Size | Font Weight | Example Text |
token-heading-xxl | Hero/Marketing Titles | var(–font-size-xxl) | var(–font-weight-bold) | Flummox |
token-heading-xl | Primary Page Titles | var(–font-size-xl) | var(–font-weight-bold) | Select a Puzzle |
token-heading-lg | Section Headers | var(–font-size-lg) | var(–font-weight-bold) | Your Statistics |
token-body-md | Main Body Copy, Paragraphs | var(–font-size-base) | var(–font-weight-regular) | This is a puzzle… |
token-body-sm | Subtext, Captions, Helper Text | var(–font-size-sm) | var(–font-weight-regular) | Tap and hold to charge. |
token-label | UI Labels, Button Text | var(–font-size-base) | var(–font-weight-medium) | Reset Puzzle |
Section 4: Analytics and Real-Time Metrics
This section details the architecture for capturing and surfacing novel, insightful gameplay metrics. It establishes a full-stack data pipeline, from client-side event generation to back-end processing and real-time dashboard updates. The design of the Key Performance Indicators (KPIs) directly informs the structure of the underlying analytics events. To effectively measure metrics like “Time-to-Epiphany” and create “Interaction Hotspots,” a unified event schema is necessary. This schema must capture not only the what and when of an interaction but also the who, where, and with what. Consequently, the fundamental analytics event payload must contain: { sessionId, playerId, puzzleId, objectId, eventType, timestamp, coordinates: { x, y } }. This forward-looking schema ensures that all logged events are rich enough to answer both current and future analytical questions.
4.1. KPI 1: “Time-to-Epiphany” (TTE)
- Definition & Rationale: Time-to-Epiphany (TTE) measures the duration between a player’s first failed interaction with a specific puzzle element and their eventual successful interaction with that same element. A high TTE might indicate a frustrating or unintuitive mechanic, while a very low TTE might suggest the puzzle is too easy. This metric is a key indicator of “desirable difficulty” and is crucial for puzzle balancing.
- Data Schema (MySQL):
First, a raw events table is needed to ingest all interactions. Then, a materialized view can be created to pre-calculate the TTE windows, significantly speeding up analytical queries.
SQL
— Raw events table for ingestion
CREATE TABLE `analytics_events` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`session_id` VARCHAR(255) NOT NULL,
`player_id` VARCHAR(255) NOT NULL,
`puzzle_id` VARCHAR(255) NOT NULL,
`object_id` VARCHAR(255) NOT NULL,
`event_type` ENUM(‘interact_fail’, ‘interact_success’, ‘puzzle_start’, ‘puzzle_complete’) NOT NULL,
`coordinates_x` SMALLINT,
`coordinates_y` SMALLINT,
`event_timestamp` TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `idx_player_puzzle_object` (`player_id`, `puzzle_id`, `object_id`)
);
— Materialized View (or a table populated by a scheduled job) for TTE calculation
CREATE TABLE `materialized_view_epiphany_windows` (
`player_id` VARCHAR(255) NOT NULL,
`puzzle_id` VARCHAR(255) NOT NULL,
`object_id` VARCHAR(255) NOT NULL,
`first_fail_timestamp` TIMESTAMP(3) NOT NULL,
`first_success_timestamp` TIMESTAMP(3) NOT NULL,
`duration_seconds` DECIMAL(10, 3) NOT NULL,
PRIMARY KEY (`player_id`, `puzzle_id`, `object_id`)
);
— Note: This table would be populated by a background process that queries `analytics_events`. - Aggregation Logic (SQL):
SQL
— Query for Rolling 60-Minute Window Average TTE (for live ops dashboard)
SELECT
puzzle_id,
AVG(duration_seconds) AS avg_tte_last_hour
FROM materialized_view_epiphany_windows
WHERE first_success_timestamp >= NOW() – INTERVAL 60 MINUTE
GROUP BY puzzle_id;
— Query for Lifetime Aggregate TTE (for deep analysis and balancing)
SELECT
puzzle_id,
AVG(duration_seconds) AS lifetime_avg_tte,
STDDEV(duration_seconds) AS lifetime_stddev_tte,
COUNT(*) AS total_epiphanies
FROM materialized_view_epiphany_windows
GROUP BY puzzle_id
ORDER BY lifetime_avg_tte DESC; - Live Dashboard Integration (Socket.io):
A background job on the Node.js server can periodically query the database and push updates to a connected admin dashboard.
JavaScript
// apps/server/src/services/analyticsScheduler.ts
import { Server as SocketIOServer } from ‘socket.io’;
import cron from ‘node-cron’;
import { db } from ‘./database’; // Your database connection module
export function startTTEBroadcaster(io: SocketIOServer) {
// Run every 15 seconds
cron.schedule(‘*/15 * * * * *’, async () => {
try {
const results = await db.query(`
SELECT puzzle_id, AVG(duration_seconds) AS avg_tte_last_hour
FROM materialized_view_epiphany_windows
WHERE first_success_timestamp >= NOW() – INTERVAL 60 MINUTE
GROUP BY puzzle_id;
`);
// Emit to a specific room for authenticated admins
io.to(‘admin_dashboard’).emit(‘kpi:tte-update’, results);
} catch (error) {
console.error(‘Failed to broadcast TTE KPI update:’, error);
}
});
}
4.2. KPI 2: “Interaction Hotspots”
- Definition & Rationale: This KPI moves beyond a simple “Most-Triggered Object” count by capturing the precise coordinates of every interaction (tap, drag_start, etc.). Aggregating this spatial data allows for the generation of heatmaps, which are powerful visualization tools. Heatmaps can reveal where players are focusing their attention, where they might be mis-clicking due to poor UI/UX, or which interactive elements are being ignored entirely.
- Data Schema (MySQL):
This KPI leverages the same analytics_events table defined for TTE, as the necessary coordinates_x and coordinates_y fields are already part of the unified event schema. No additional tables are required for raw data storage. - Aggregation Logic (SQL):
The key to generating heatmap data from SQL is to grid the coordinates, grouping many individual points into larger, countable cells.
SQL
— Query for Rolling 60-Minute Window Hotspots (for live heatmap)
— The ’20’ value determines the grid size for the heatmap cells.
SELECT
puzzle_id,
FLOOR(coordinates_x / 20) AS grid_x,
FLOOR(coordinates_y / 20) AS grid_y,
COUNT(*) AS interaction_count
FROM analytics_events
WHERE
event_timestamp >= NOW() – INTERVAL 60 MINUTE
AND coordinates_x IS NOT NULL
AND event_type IN (‘interact_fail’, ‘interact_success’)
GROUP BY puzzle_id, grid_x, grid_y;
— Query for Lifetime Aggregate Hotspots (for overall puzzle analysis)
SELECT
puzzle_id,
FLOOR(coordinates_x / 20) AS grid_x,
FLOOR(coordinates_y / 20) AS grid_y,
COUNT(*) AS total_interactions
FROM analytics_events
WHERE
coordinates_x IS NOT NULL
AND event_type IN (‘interact_fail’, ‘interact_success’)
GROUP BY puzzle_id, grid_x, grid_y
ORDER BY total_interactions DESC; - Live Dashboard Integration (Socket.io):
For a live heatmap, it’s more efficient to push new interaction points as they happen rather than re-querying the entire grid. The server can batch these and send them out periodically.
JavaScript
// apps/server/src/sockets/eventHandlers.ts
import { Socket } from ‘socket.io’;
// This would be part of a larger system that buffers points
let interactionPointBuffer: { puzzleId: string, point: { x: number, y: number } } =;
// Called when a new analytics event is ingested by the server
export function handleNewInteractionEvent(event: any) {
if (event.coordinates_x && event.coordinates_y) {
interactionPointBuffer.push({
puzzleId: event.puzzleId,
point: { x: event.coordinates_x, y: event.coordinates_y }
});
}
}
// A separate broadcaster function, perhaps on a timer
export function broadcastHotspotUpdates(io: SocketIOServer) {
// Run every 5 seconds
setInterval(() => {
if (interactionPointBuffer.length > 0) {
// In a real app, you’d group these by puzzleId before emitting
io.to(‘admin_dashboard’).emit(‘kpi:hotspot-update’, {
newPoints: interactionPointBuffer
});
interactionPointBuffer =; // Clear the buffer
}
}, 5000);
}
Section 5: Quality, Automation, and Deployment Readiness
This section establishes the framework for ensuring code quality, application stability, and high performance. It implements a “shift-left” philosophy by integrating testing, security, and performance considerations directly into the development workflow. The Continuous Integration (CI) pipeline is configured not just as a test runner, but as an automated policy enforcement engine. This elevation of the CI’s role is critical for maintaining high standards, especially in a workflow that incorporates AI-generated code. By creating automated gates for both technical correctness and process compliance, the pipeline becomes the ultimate guardian of project quality.
5.1. Automated Testing Suite
A multi-layered testing strategy is essential for confidence in releases. This includes unit tests for isolated logic, integration tests for component interactions, and end-to-end tests for user flows.
5.1.1. Jest Physics Snapshot Tests
Snapshot tests are exceptionally effective for catching regressions in complex, deterministic systems like a physics engine. This test programmatically loads a puzzle scene, steps the physics engine forward by a fixed amount, and then saves a “snapshot” of the positions and velocities of all dynamic bodies. Any future code change that unintentionally alters the physics simulation will cause the snapshot test to fail, immediately alerting the developer.
TypeScript
// apps/client/src/scenes/__tests__/PuzzleScene.physics.test.ts
import { PuzzleScene } from ‘../PuzzleScene’;
// Mock Phaser and Matter.js dependencies for a Node.js environment
// This requires a setup like ‘jest-phaser-mock’
describe(‘PuzzleScene Physics Simulation’, () => {
it(‘should result in a predictable state after 500ms of simulation for p001’, () => {
// 1. Setup: Load the scene with the specific puzzle data
const scene = new PuzzleScene();
// Manually inject or mock the loading of ‘p001_aligned_gateway.json’
scene.sys.init({ puzzleId: ‘p001’ });
scene.create({ puzzleId: ‘p001’ });
// 2. Action: Step the physics world forward by a fixed amount
const simulationSteps = 30; // 30 steps at ~16.67ms per step
for (let i = 0; i < simulationSteps; i++) {
scene.matter.world.step(16.666);
}
// 3. Assertion: Create a snapshot of the dynamic bodies’ state
const dynamicBodiesState =;
for (const [id, gameObject] of scene.gameObjects.entries()) {
if (gameObject.body &&!(gameObject.body as MatterJS.BodyType).isStatic) {
const body = gameObject.body as MatterJS.BodyType;
dynamicBodiesState.push({
id,
position: { x: body.position.x.toFixed(2), y: body.position.y.toFixed(2) },
velocity: { x: body.velocity.x.toFixed(2), y: body.velocity.y.toFixed(2) },
angle: body.angle.toFixed(2)
});
}
}
expect(dynamicBodiesState).toMatchSnapshot();
});
});
5.1.2. Cypress End-to-End Test Stubs
E2E tests simulate real user interactions with the application running in a browser. These stubs provide a clear starting point for testing critical user flows. They use data-cy attributes as selectors, which is a best practice as it decouples tests from fragile CSS classes or DOM structure.
username-flow.cy.ts
TypeScript
// apps/e2e/cypress/e2e/username-flow.cy.ts
describe(‘Username and Login Flow’, () => {
it(‘should allow a user to enter a username and see it displayed on the home screen’, () => {
cy.visit(‘/’);
cy.get(‘[data-cy=”username-input”]’).type(‘PlayerOne’);
cy.get(‘[data-cy=”username-submit-button”]’).click();
cy.get(‘[data-cy=”welcome-message”]’).should(‘contain.text’, ‘Welcome, PlayerOne’);
});
});
fail-redirect.cy.ts
TypeScript
// apps/e2e/cypress/e2e/fail-redirect.cy.ts
describe(‘Authentication Redirects’, () => {
it(‘should redirect an unauthenticated user from a protected route to the login page’, () => {
// Attempt to visit a protected route, e.g., the puzzle selection screen
cy.visit(‘/puzzles’);
// Assert that the URL is now the login or home page
cy.url().should(‘include’, ‘/login’); // Or ‘/’ depending on the flow
cy.get(‘[data-cy=”login-prompt”]’).should(‘be.visible’);
});
});
live-stats.cy.ts
TypeScript
// apps/e2e/cypress/e2e/live-stats.cy.ts
describe(‘Admin Live Stats Dashboard’, () => {
beforeEach(() => {
// Stub the Socket.io connection or log in as an admin user
cy.loginAsAdmin(); // Custom command to handle admin authentication
cy.visit(‘/admin/dashboard’);
});
it(‘should connect to the stats socket and display KPI containers’, () => {
// Check for the existence of the UI elements that will hold the live data
cy.get(‘[data-cy=”kpi-tte-container”]’).should(‘be.visible’);
cy.get(‘[data-cy=”kpi-hotspot-container”]’).should(‘be.visible’);
cy.get(‘[data-cy=”kpi-connection-status”]’).should(‘have.text’, ‘Connected’);
});
});
5.2. Continuous Integration and Deployment (CI/CD)
The CI pipeline is the automated gatekeeper for the main branch. The following GitHub Actions workflow ensures that no code is merged without passing all quality checks, including the crucial human review process for AI-generated code.
5.2.1. GitHub Actions Workflow (.github/workflows/ci.yml)
This YAML file defines a multi-job workflow that runs on every pull request targeting the main branch. The final merge is contingent on the success of all jobs, including the check-for-review process gate.
YAML
#.github/workflows/ci.yml
name: Flummox CI
on:
push:
branches:
– main
pull_request:
branches:
– main
jobs:
lint-and-format:
name: Lint & Format Check
runs-on: ubuntu-latest
steps:
– name: Checkout code
uses: actions/checkout@v3
– name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
– name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ’18’
cache: ‘pnpm’
– name: Install dependencies
run: pnpm install
– name: Run lint and format check
run: pnpm lint
unit-tests:
name: Unit & Integration Tests
runs-on: ubuntu-latest
needs: lint-and-format
steps:
– name: Checkout code
uses: actions/checkout@v3
– name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
– name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ’18’
cache: ‘pnpm’
– name: Install dependencies
run: pnpm install
– name: Run Jest tests
run: pnpm test
e2e-tests:
name: End-to-End Tests
runs-on: ubuntu-latest
needs: lint-and-format
steps:
– name: Checkout code
uses: actions/checkout@v3
– name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
– name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ’18’
cache: ‘pnpm’
– name: Install dependencies
run: pnpm install
– name: Run Cypress tests
uses: cypress-io/github-action@v5
with:
build: pnpm build
start: pnpm start # Command to start both client and server
wait-on: ‘http://localhost:5173’ # Wait for the client to be ready
check-for-review:
name: Verify Human Review
runs-on: ubuntu-latest
# This job is the process gate. It checks for the #human-review label on the PR.
# In GitHub’s branch protection rules, this job MUST be set as “required” to pass.
if: github.event_name == ‘pull_request’
steps:
– name: Check for human-review label
uses: actions/github-script@v6
with:
script: |
const labels = context.payload.pull_request.labels.map(label => label.name);
if (!labels.includes(‘human-review’)) {
core.setFailed(“This PR is missing the ‘#human-review’ label. Please add it after thoroughly reviewing all AI-generated code.”);
} else {
core.info(“Found ‘#human-review’ label. Proceeding.”);
}
5.3. Security & Performance Hardening Checklist
This checklist provides a single, comprehensive reference for essential hardening measures. Each item includes its rationale and implementation location within the monorepo, serving as a guide for initial setup and ongoing audits.
Area | Tool / Technique | Purpose / Rationale | Implementation Snippet / File Path |
Security | helmet | Sets various security-related HTTP headers (e.g., X-Content-Type-Options, Strict-Transport-Security) to prevent common web vulnerabilities. | app.use(helmet()); in apps/server/src/index.ts |
Security | express-rate-limit | Prevents brute-force attacks against authentication and other sensitive endpoints by limiting repeated requests from the same IP. | app.use(‘/api/auth’, rateLimit({ windowMs: 15 * 60 * 1000, max: 100 })); in apps/server/src/index.ts |
Security | SQL Sanitization | Prevents SQL injection attacks by ensuring user input is never directly concatenated into SQL queries. Always use parameterized queries. | db.query(‘SELECT * FROM users WHERE id =?’, [userId]); using the mysql2 library in any database service file. |
Security | cors | Enforces a strict Cross-Origin Resource Sharing policy, ensuring the API only responds to requests from authorized origins (i.e., the web client). | app.use(cors({ origin: ‘https://flummox.app’ })); in apps/server/src/config/cors.ts |
Performance | CDN for Static Assets | Serves static assets (images, JS/CSS bundles) from a Content Delivery Network, reducing latency for users globally and offloading the server. | Configure the build process to prefix asset URLs with the CDN host. E.g., in Vite config: base: ‘https://cdn.flummox.app/’ |
Performance | WASM Lazy-loading | For heavy computations (like a custom physics engine), compile to WebAssembly (WASM) and load it asynchronously only when needed to reduce initial load time. | const { physicsEngine } = await import(‘./heavy-physics.wasm’); inside the relevant Phaser scene or service. |
Performance | Bundle Size Thresholds | Uses tools like webpack-bundle-analyzer to visualize bundle composition and sets size limits in CI to prevent accidental inclusion of large libraries. | Add a script pnpm analyze and a CI step that fails if main.js exceeds a threshold, e.g., 250kb. |
Performance | Image Optimization | Compresses images and serves them in modern formats like WebP to significantly reduce page weight and improve load times. | Use an image processing library or a build tool plugin (e.g., vite-imagetools) to automate optimization during the build process. |
Section 6: AI-Augmented Development Workflow
This final section provides the strategic framework for formally integrating generative AI into the development process. The goal is to transform AI from an ad-hoc tool into a tracked, auditable, and efficient part of the workflow. This is achieved through a combination of structured prompts (a “Cookbook”) and a clear contribution protocol.
6.1. Prompt-Engineering Cookbook
This cookbook contains five reusable prompt templates designed to elicit high-quality, context-aware code and content from a large language model like Gemini. Each prompt is structured for easy copy-pasting and includes guidance on how to provide the necessary context for optimal results.
Template 1: New Puzzle Type Generation
- Tags: #ai-draft #rob-revise
- Prompt:
Act as a senior game designer and TypeScript developer for the “Flummox” project. Your task is to design a new puzzle entity type.
**Context:**
The game uses a JSON-based puzzle definition schema and a Phaser 3 scene factory to instantiate entities.
Here is the existing `PuzzleEntity` TypeScript interface from `@flummox/shared-types`:
[Paste the full PuzzleEntity type definition here]
Here is an example of an existing puzzle JSON file (`p002_resonant_crystal.json`):
Here is the relevant switch statement from `PuzzleScene.ts` that creates entities:
[Paste the `createEntities` method’s `switch` block here]
**Request:**
1. Design a new puzzle entity type called “ToggleButton” that, when clicked, toggles a target entity between a `visible` and `invisible` state.
2. Update the `PuzzleEntity` TypeScript interface to include this new `ToggleButton` type and its specific properties (e.g., `targetId`, `initialState`).
3. Create a new example puzzle definition file, `p003_fading_bridge.json`, that uses this new “ToggleButton” to make a “StaticBox” (acting as a bridge) appear and disappear.
4. Provide the new `case ‘ToggleButton’:` block that should be added to the `PuzzleScene.ts` factory method to handle the creation and interactive logic for this new entity. The logic should use Phaser’s `setInteractive()` and `on(‘pointerdown’,…)` methods.
Template 2: Jest Unit Test Generation
- Tags: #ai-draft #rob-revise
- Prompt:
Act as a senior software engineer specializing in test-driven development with TypeScript and Jest.
**Context:**
I have the following TypeScript utility function located at `apps/server/src/utils/analyticsHelper.ts`. This function is responsible for validating incoming analytics event payloads.
[Paste the full source code of the analyticsHelper.ts file here]
**Request:**
Write a complete Jest test file (`analyticsHelper.test.ts`) for this utility. The test suite should cover the following cases:
1. A test for a valid, complete event payload that should pass validation.
2. A test for a payload with a missing `playerId` that should fail validation.
3. A test for a payload with an invalid `eventType` enum value that should fail.
4. A test for a payload where `coordinates_x` is present but `coordinates_y` is missing.
5. A test for a payload with a future `event_timestamp` that should be considered invalid.
Use `describe`, `it`, and `expect` syntax.
Template 3: React Component Refactoring
- Tags: #ai-draft #rob-revise
- Prompt:
Act as a senior front-end developer with expertise in React, TypeScript, and modern hooks.
**Context:**
I have a React component, `UserProfile.tsx`, that fetches user data using `useEffect` and manages loading/error states with multiple `useState` hooks. The code is becoming difficult to manage.
Here is the current component code:
[Paste the full source code of UserProfile.tsx here]
**Request:**
1. Refactor this component into a custom hook called `useUserProfile(userId)`.
2. The custom hook should encapsulate all the data fetching logic, loading state, and error state. It should return an object like `{ user, isLoading, error }`.
3. The state management within the hook should be simplified by using a `useReducer` hook instead of multiple `useState` hooks.
4. Rewrite the `UserProfile.tsx` component to be a simple, clean component that consumes the new `useUserProfile` hook.
Template 4: SQL Query Optimization
- Tags: #ai-draft #rob-revise
- Prompt:
Act as an expert MySQL database administrator (DBA) specializing in query performance optimization.
**Context:**
I have the following two table schemas: `users` and `puzzle_completions`.
I am running the following query to get the top 10 users with the fastest average completion time for puzzles of ‘hard’ difficulty. The query is running slowly on a large dataset.
**Request:**
1. Analyze the provided query and schemas.
2. Identify potential performance bottlenecks (e.g., joins, aggregations, lack of indexes).
3. Provide an optimized version of the SQL query.
4. Recommend any necessary database indexes that should be added to the tables to support the optimized query. Provide the `CREATE INDEX` statements. Explain why each index is beneficial.
Template 5: Cypress E2E Test Case Generation
- Tags: #ai-draft #rob-revise
- Prompt:
Act as a QA automation engineer specializing in Cypress and end-to-end testing.
**Context:**
The Flummox application has a puzzle selection screen at the `/puzzles` route. The screen displays a list of puzzles as cards. Each card has a `data-cy=”puzzle-card”` attribute. Inside each card, there is a title with `data-cy=”puzzle-name”` and a difficulty rating with `data-cy=”puzzle-difficulty”`. Clicking a card navigates the user to `/play/{puzzleId}`.
**Request:**
Write a new Cypress test file named `puzzle-selection.cy.ts`. The test file should contain the following tests for the puzzle selection flow:
1. A test that verifies the puzzle list page loads and displays at least one puzzle card.
2. A test that searches for the puzzle named “The Resonant Crystal”, asserts its difficulty is “3”, and then clicks on it.
3. After clicking, the test should assert that the URL has changed to `/play/p002_resonant_crystal`.
4. The test should then verify that the Phaser game canvas with `data-cy=”game-canvas”` is visible on the new page.
6.2. AI-Contribution Tracking Protocol
To integrate AI assistance responsibly, a formal tracking protocol is mandatory. This protocol ensures that all AI-generated code undergoes rigorous human oversight before being merged into the main codebase, creating a clear and auditable trail of human-AI collaboration. This protocol will be documented in CONTRIBUTING.md and enforced by the CI pipeline.
- Commit Labeling:
A two-tag system is used in commit messages to track the lifecycle of AI-generated code.
- #ai-draft: This tag must be used in the first commit message of any code that was substantially generated by an AI tool. It serves as a clear signal to all team members that the code is a preliminary draft and has not yet been validated or accepted by a human developer.
- Example Commit: feat: Add initial user profile page layout #ai-draft
- #rob-revise (Robot-revise): This tag must be used in a subsequent commit by a human developer after they have thoroughly reviewed, tested, refactored, and taken complete ownership of the code introduced in an #ai-draft commit. This commit signifies that the code now meets project standards for quality, security, and performance.
- Example Commit: refactor: Validate and harden user profile page #rob-revise
- Pull Request Policy:
The commit tagging system is integrated directly into the pull request and CI workflow.
- Work in Progress: A pull request containing any commits with an #ai-draft tag that does not have a corresponding #rob-revise commit is considered a “Work in Progress” and is not ready for final review.
- Ready for Review: A pull request is only ready for merge when all AI-generated code has been fully reviewed and accepted, which is signified by a #rob-revise commit that follows the initial #ai-draft commit.
- CI Merge Gate: To signal that this human review process is complete, the developer must add the #human-review label to the pull request on GitHub. The CI pipeline (as defined in ci.yml) is configured to fail if this label is not present, effectively blocking the merge. This creates an unbreakable automated gate that enforces the human-in-the-loop review process.