Skip to content

Latest commit

 

History

History
546 lines (414 loc) · 32.5 KB

File metadata and controls

546 lines (414 loc) · 32.5 KB

Development Status & Next Steps

Project: Signal Processing Puzzle Game Date: 2026-02-04 Stack: Canvas 2D + React 19 + Zustand + TypeScript + Vite


Current Phase: UX Redesign Planning Complete

The original game is fully implemented. A comprehensive UX redesign has been planned and architectured. Architecture validation (Step 1) and epic/story updates (Step 2) are complete. The next step is to check implementation readiness (Step 3) and then begin sprint planning (Step 4).


What's Been Built (Original Implementation)

All 4 epics are complete. 505 tests passing across 34 test suites.

Epic 1: Interactive Signal Sandbox

Nodes, wires, Canvas rendering, palette, waveform visualization, WTS timing.

Epic 2: Puzzle Play

Puzzle loading, continuous validation, formula baking, completion ceremony, baked node runtime.

Epic 3: Node Building & Navigation

Zoom navigation, zoom transitions, breadcrumbs, utility nodes, library sync.

Epic 4: Progression & Persistence

45+ levels across 4 arcs, save/load, undo/redo, progression system.

Implemented Modules

Module Files Status
engine/ 21 Complete -- nodes, graph, baking
puzzle/ 23 Complete -- levels, validation, waveform generators
store/ 21 Complete -- 9 Zustand slices, persistence, hot-replace
gameboard/ 12 Complete -- Canvas rendering, hit-testing
shared/ 9 Complete -- types, math, logger, Result
wts/ 7 Complete -- clock, tick scheduler
ui/ 8 Complete -- controls, puzzle UI, navigation
palette/ 3 Complete -- panel, node definitions
simulation/ 1 Complete -- simulation controller

Empty Stub Directories (Not Yet Needed)

debug/, validation/, persistence/, progression/, assets/styles/

These have directory structures but no files. Their functionality either lives inline in store slices or is deferred to the redesign.


UX Redesign Scope

The redesign replaces the sidebar-based UI with a full-screen immersive experience. No new npm dependencies. Everything is pure TS + Canvas 2D + CSS Modules on the existing stack.

6 New Architectural Decisions

# Decision Summary
1 Auto-Routing A* pathfinding on 64x36 grid (H/V/45-degree only)
2 Analog Meters Three-channel Canvas meters with circular buffer
3 Lid Animation Vertical clamshell double-door zoom transition
4 Token Cache Flat typed object, CSS vars read once per theme switch
5 Wire Rendering Polarity color gradient + peak glow beyond +/-75
6 Viewport 16:9 locked, 64x36 grid (doubled density), letterboxed

17 New Modules to Create

Module Purpose
shared/grid/ Grid/pixel coordinate types + conversion
shared/tokens/ ThemeTokens type + cache builder
shared/routing/ A* auto-router + occupancy helpers
gameboard/meters/ Three-channel analog meter rendering
gameboard/animation/ Lid, ceremony, zoom draw functions
gameboard/interaction/ Mouse, keyboard, focus, placement ghost
gameboard/canvas/render-grid.ts Grid zone rendering
assets/styles/tokens.css CSS custom properties (source of truth)
assets/styles/theme-dark.css Signal Bench theme
assets/styles/theme-light.css Studio Monitor theme
assets/styles/animations.css Animation tokens (reduced-motion aware)
store/slices/overlay-slice.ts activeOverlay discriminated union
store/slices/animation-slice.ts Lid + ceremony state machines
store/slices/meter-slice.ts Meter state + circular buffer refs
store/slices/routing-slice.ts Wire paths, reroute triggers
ui/overlays/ Modals, popovers, context menus
ui/layout/GameLayout.tsx Top-level layout + overlay host

Planning Artifacts

All planning documents live in _bmad-output/.

Document Location Status
Game Design Document signal_puzzle_game_design.md (root) Complete
Original Architecture _bmad-output/game-architecture.md Complete
Project Context _bmad-output/project-context.md Complete (34 rules)
Epics & Stories _bmad-output/planning-artifacts/epics.md Updated (redesign epics 5-7 added)
Architecture Validation _bmad-output/planning-artifacts/architecture-validation-report.md Pass (3 moderate, 4 minor)
UX Design Spec _bmad-output/planning-artifacts/ux-design-specification.md Complete
UX Validation Report _bmad-output/planning-artifacts/ux-validation-report.md Pass
Redesign Architecture _bmad-output/planning-artifacts/architecture.md Complete
Interactive Demos _bmad-output/planning-artifacts/architecture-explorations.html Complete

Next Steps (BMAD Method)

The project is at the boundary between Phase 3 (Solutioning) and Phase 4 (Implementation). The redesign architecture is validated, and epics/stories are updated with redesign scope. Implementation readiness must be verified before sprint planning begins.

Run each workflow in a fresh context window.

Step 1: Validate Architecture -- DONE

Architecture validated. Report at _bmad-output/planning-artifacts/architecture-validation-report.md. Verdict: PASS with 3 moderate migration-scope findings (Wire type, NodeState position, render layer rewrite) and 4 minor notes. No changes needed to the architecture document itself.


Step 2: Update Epics & Stories -- DONE

Epics updated at _bmad-output/planning-artifacts/epics.md. Added:

  • 15 new redesign FRs (FR34-FR48), 2 new NFRs (NFR11-12), 6 new ARs (AR14-19)
  • Epic 5: Redesign Foundation (5 stories: grid, tokens, type migrations, render loop, grid zones)
  • Epic 6: Redesign Signal Visualization (4 stories: meters, auto-routing, wire rendering, node rendering)
  • Epic 7: Redesign Interaction & Navigation (5 stories: overlays, palette/popover/context, lid animation, ceremony, keyboard/a11y)
  • Redesign impact notes on 8 original stories (1.5, 1.6, 1.7, 1.8, 2.1, 2.2, 2.4, 4.4)
  • FR20 updated: victory threshold 2 cycles -> 1 cycle
  • Full dependency graph for redesign stories

Step 3: Check Implementation Readiness -- DONE

Implementation readiness assessed. Report at _bmad-output/planning-artifacts/implementation-readiness-report.md. Verdict: READY with 4 minor advisories (no blockers).

  • 100% FR coverage (48/48 FRs mapped to epics/stories)
  • 0 forward dependencies, 0 unresolved contradictions
  • 4 resolved cross-document contradictions (breadcrumbs, victory threshold, lid animation, Return to Puzzle)
  • Advisories: Stories 5.3, 7.2, 7.5 oversized (split during sprint planning); Epic 5 infrastructure-framed

Step 4: Sprint Planning -- DONE

Sprint status generated at _bmad-output/implementation-artifacts/sprint-status.yaml.

  • 7 epics, 32 stories total (18 done from original, 14 backlog for redesign)
  • Epics 1-4 preserved as done; Epics 5-7 added as backlog
  • Recommended 8-sprint sequence respecting dependency graph
  • Sprint 1 starts with Stories 5.1 (Grid System) + 5.2 (Token System)

Step 5: Story Cycle (Repeating, In Progress)

For each story in the sprint plan:

  1. Create Story -- /bmad-bmm-create-story Prepare the story with full implementation details.

  2. Validate Story (optional) -- /bmad-bmm-create-story (Validate Mode) Independent review of story readiness.

  3. Dev Story -- /bmad-bmm-dev-story Implement the story. Write code and tests.

  4. Code Review (optional) -- /bmad-bmm-code-review Review implementation quality.

  5. Repeat for next story, or run Retrospective at epic boundaries.


Sprint 1 Progress

Story 5.1: Grid Coordinate System & Viewport -- DONE

Story file: _bmad-output/implementation-artifacts/5-1-grid-coordinate-system-viewport.md

  • Created src/shared/grid/ module (types, conversions, constants, viewport, barrel export)
  • GameboardCanvas: 16:9 viewport lock, letterbox centering, cellSize ref, rAF-debounced resize, too-small warning
  • render-loop: getCellSize callback, derives dimensions from GRID_COLS/ROWS * cellSize
  • 25 new tests, 530 total passing, zero regressions, TypeScript clean

Story 5.2: Design Token System & Dual Themes -- DONE

Story file: _bmad-output/implementation-artifacts/5-2-design-token-system-dual-themes.md

  • Created 4 CSS files: tokens.css (27 vars), theme-dark.css (Signal Bench), theme-light.css (Studio Monitor), animations.css (reduced-motion aware)
  • Created src/shared/tokens/ module: TOKEN_KEYS (27 keys), TokenKey type, ThemeTokens type, buildThemeTokens(), theme-manager (initTheme/setTheme/getThemeTokens + reduced-motion listener)
  • Updated main.tsx: imports CSS, calls initTheme('dark') before render
  • 21 new tests, 551 total passing, zero regressions, TypeScript clean

Story 5.3: Core Type Migrations & Connection Point Configuration -- DONE

Story file: _bmad-output/implementation-artifacts/5-3-core-type-migrations-connection-point-configuration.md

  • Wire type migrated: from/to → source/target, signals → signalBuffer ring buffer (16 entries) + writeHead, added path: GridPoint[]
  • NodeState.position: Vec2 → GridPoint; all render functions accept cellSize for grid→pixel conversion
  • Occupancy grid (boolean[32][18]) added to gameboard slice, maintained on add/remove node
  • ConnectionPointConfig type + builder functions for puzzle and custom node gameboards
  • VICTORY_CYCLES updated 2→1, Signal type removed, tick scheduler rewritten for ring buffer
  • 16 new tests, 568 total passing across 38 suites, zero TypeScript errors

Story 5.4: Render Loop & Draw Function Refactor -- DONE

  • All 5 draw functions refactored to (ctx, tokens, stateSlice, ...params) signature
  • render-loop.ts is sole Zustand bridge: single getState() + getThemeTokens() per frame
  • All COLORS constants replaced with ThemeTokens across all render files
  • useGameStore removed from all draw files (render-nodes, render-wires, render-connection-points, render-waveforms, render-wire-preview)
  • New files: render-types.ts (typed state-slice interfaces), render-draw-signatures.test.ts (32 contract tests)
  • 32 new tests, 600 total passing across 39 suites, zero TypeScript errors

Story 5.5: Grid Zone & Background Rendering -- DONE

Story file: _bmad-output/implementation-artifacts/5-5-grid-zone-background-rendering.md

  • Created src/gameboard/canvas/render-grid.ts with drawGrid(ctx, tokens, state, cellSize)
  • Renders 3 zone backgrounds: playable area (gridArea), left meter zone (meterHousing), right meter zone (meterHousing)
  • Draws vertical grid lines in playable area (cols 3-28) and horizontal lines across full gameboard
  • Supports gridOpacity for zoom animation dimming; restores globalAlpha after draw
  • Integrated into render-loop.ts as first draw call (lowest z-order, after canvas clear)
  • Added RenderGridState interface to render-types.ts
  • 21 new tests (16 unit + 5 contract), 621 total passing across 40 suites, zero TypeScript errors

Epic 5 Complete

All 5 stories in Epic 5 (Redesign Foundation) are done. Next: Epic 6 (Redesign Signal Visualization).


Sprint 2 Progress (Epic 6)

Story 6.1: Analog Meter Rendering -- DONE

  • Created src/gameboard/meters/ module: meter-types.ts (constants, types, MeterKey), circular-buffer.ts (Float64Array(128) ring buffer), barrel index.ts
  • 5 render functions: render-waveform-channel.ts (scrolling polarity-colored fill with 3-stop opacity), render-level-bar.ts (center-outward bar), render-needle.ts (glow line), render-target-overlay.ts (dashed stroke), render-meter.ts (compositor with channel layout ratios)
  • Created src/store/slices/meter-slice.ts: MeterSlice with initializeMeters, setMeterVisualState, resetMeters actions
  • Integrated into store/index.ts (GameStore union), simulation-controller.ts (meter buffer init/record/clear + initializeMeters call), render-loop.ts (meter drawing after grid with derived confirming/mismatch visual states)
  • Added RenderMetersState to render-types.ts; extended contract tests in render-draw-signatures.test.ts
  • 22 new tests (8 circular-buffer, 7 meter-slice, 5 render-meter, 2 contract), 643 total passing across 43 suites, zero TypeScript errors

Story 6.2: Wire Auto-Routing Engine -- DONE

  • Created src/shared/routing/ module: grid-graph.ts (8-direction constrained graph, passability, Chebyshev heuristic), auto-router.ts (A* pathfinding with direction-tracking state space, binary min-heap PQ, turn penalty, horizontal entry/exit enforcement), barrel index.ts
  • A* state space is (col, row, direction) -- 8 directions x 576 cells. Only 0-degree and 45-degree turns allowed per step; 90-degree and wider turns impossible by construction.
  • getPortGridAnchor(node, side, portIndex) computes grid cell for any port (regular nodes: 1 cell outside bounding box; connection points: at playable area boundary cols 3/28)
  • Created src/store/slices/routing-slice.ts: routeAllWires() action computes A* paths for all wires in a single batch update via updateWires()
  • initRouting() subscriber auto-triggers routing on graphVersion or activeBoardId changes
  • Integrated into store/index.ts (GameStore union, initRouting call after initHistory)
  • 46 new tests (20 grid-graph, 19 auto-router, 7 routing-slice), 689 total passing across 46 suites, zero TypeScript errors

Story 6.3: Wire Signal Rendering -- DONE

  • Rewrote render-wires.ts: bezier curves replaced with polyline rendering along wire.path grid routes
  • Three-pass rendering recipe per wire: neutral base (0.4 alpha) → glow halo (|signal|>75, shadowBlur 0→12) → polarity color per segment
  • Color interpolation: hexToRgb + lerpColor helpers; neutral→polarity gradient over signal range 0–75, clamped beyond
  • Ring buffer → segment mapping: 16-sample buffer mapped proportionally across path segments (source=newest, target=oldest)
  • Export renamed renderWiresdrawWires(ctx, tokens, wires, cellSize); render-loop call site updated
  • 31 new tests, 722 total passing across 47 suites, zero TypeScript errors

Story 6.4: Node Grid Rendering & Sizing -- DONE

  • Three node size categories: Fundamental (3x2), Puzzle (3x dynamic rows), Utility (5x3)
  • getNodeGridSize(node) computes per-node grid footprint; canPlaceNode accepts explicit cols/rows
  • NODE_CONFIG replaced with ratio-based NODE_STYLE (all dimensions scaled to cellSize at render time)
  • render-nodes.ts fully rewritten: gradient fills, drop shadows, visual states (default/hover/selected), two-pass draw order
  • Port positions, hit-testing, and auto-router updated for variable node dimensions
  • Added hoveredNodeId to interaction slice + wired into GameboardCanvas mouse move
  • hexToRgb/lerpColor duplicated inline in render-nodes (same as render-wires)
  • 31 new tests (12 render-nodes, 5 port-positions, 5 hit-testing, 9 occupancy), 753 total passing across 50 suites, zero TypeScript errors

Epic 6 Complete

All 4 stories in Epic 6 (Redesign Signal Visualization) are done. Next: Epic 7 (Redesign Interaction & Navigation).


Sprint 3 Progress (Epic 7)

Story 7.1: Overlay System & Focus Management -- DONE

  • Created src/store/slices/overlay-slice.ts: ActiveOverlay discriminated union (7 variants), ContextTarget type, openOverlay/closeOverlay/isOverlayEscapeDismissible/hasActiveOverlay actions, ESCAPE_IMMUNE set for save-dialog and unsaved-changes
  • Created src/gameboard/interaction/ module: focus-manager.ts (singleton focus context tracking, save/restore, Tab trap), escape-handler.ts (pure 5-level escape cascade function), barrel index.ts
  • Wired overlay slice into store/index.ts (GameStore union), updated 5 test files with full slice composition
  • GameboardCanvas.tsx: Escape handler replaced with handleEscape() cascade, overlay guards on mouse/keyboard handlers, undo/redo gated by hasActiveOverlay()
  • render-loop.ts: wire preview suppressed during overlay, semi-transparent dim (rgba(0,0,0,0.15)) drawn over canvas when overlay active
  • 39 new tests (14 overlay-slice, 11 focus-manager, 14 escape-handler), 792 total passing across 53 suites, zero TypeScript errors

Story 7.2: Palette Modal, Parameter Popover & Context Menu -- DONE

  • Replaced sidebar-based UI (PalettePanel, NodeControls) with overlay-based components
  • Created src/shared/grid/cell-size-ref.ts (module-level getter/setter for cellSize, singleton pattern)
  • Extended hit-testing with wire hit detection (point-to-segment distance, ~6px threshold)
  • Added getNodeGridSizeFromType() for placement validation without full NodeState
  • Created src/gameboard/canvas/render-placement-ghost.ts: semi-transparent grid-snapped preview with occupancy validation (red tint if invalid), clamped to playable area
  • Created src/ui/overlays/ module: PaletteModal (search + sections + keyboard nav), ContextMenu (node/wire/empty targets), ParameterPopover (mix select, delay select, threshold range)
  • Created pure-logic builders: palette-items.ts (build + filter), context-menu-items.ts (per-target items), popover-position.ts (4-side flip logic)
  • Updated GameboardCanvas.tsx: N/Space opens palette, Enter opens parameter popover, right-click context menu (node → menu, wire → menu, empty → palette), occupancy validation on placement, setCellSize on resize
  • Updated App.tsx: removed PalettePanel/NodeControls sidebar, full-screen canvas layout with overlay components
  • 43 new tests (10 palette-items, 10 context-menu-items, 8 popover-position, 9 render-placement-ghost, 6 wire hit-testing), 835 total passing across 57 suites, zero TypeScript errors

Story 7.3: Lid-Open Clamshell Zoom Animation -- DONE

  • Created src/store/slices/animation-slice.ts: LidAnimationState discriminated union (idle | opening | closing), actions: startLidOpen, startLidClose, setLidProgress, endLidAnimation; only one animation at a time (idle guard)
  • Created src/gameboard/animation/ module: lid-animation.ts with drawLidAnimation(ctx, tokens, state, progress, canvasW, canvasH), computeProgress(startTime, now, durationMs), parseDurationMs(token), ease-in-out cubic easing, shadow gradients on inner edges
  • Opening effect: parent board snapshot splits vertically at center, left/right halves compress toward edges revealing live child board behind
  • Closing effect: child board snapshot shrinks from center, revealing live parent board behind
  • Updated render-loop.ts: lid animation progress computed from rAF timestamp + animZoomDuration token, animation overlay drawn on top of live board, auto-completes at progress 1.0
  • Refactored escape-handler.ts: separated getEscapeAction() (pure) from executeEscapeAction() for pre-flight snapshot capture
  • Updated GameboardCanvas.tsx: escape-key zoom-out captures OffscreenCanvas snapshot + calls startLidClose before zoomOut()
  • Updated ContextMenu.tsx: "Edit" and "Inspect" actions capture OffscreenCanvas + call startLidOpen before board switch (replaces old startZoomTransition CSS approach)
  • Reduced motion: parseDurationMs("0ms") → 0, computeProgress returns 1 instantly → animation completes in single frame
  • 38 new tests (17 animation-slice, 21 lid-animation), 873 total passing across 59 suites, zero TypeScript errors

Story 7.4: Validation Ceremony Animation -- DONE

  • Multi-phase ceremony animation: streak → victory-burst → name-reveal → zoom-out → inactive
  • ValidationCeremonyState discriminated union added to animation-slice.ts with 7 guarded actions (startCeremonyStreak, advanceCeremonyStreak, breakCeremonyStreak, startVictoryBurst, startNameReveal, startCeremonyZoomOut, endCeremony)
  • Created src/gameboard/animation/validation-ceremony.ts: drawVictoryBurst (radial gradient flash), drawNameReveal (centered text fade-in + scale-up), drawStreakCounter (progress bar near bottom)
  • Ceremony tokens: animCeremonyBurstDuration (300ms), animCeremonyRevealDuration (500ms), both 0ms for reduced motion
  • render-loop.ts: ceremony phases auto-advance, streakPulseAlpha enhances meter confirming borders during streak, zoom-out reuses drawLidAnimation with synthesized closing state
  • simulation-controller.ts: validateTick drives streak animation (start/advance/break), triggerCeremony defers palette addition to handleCeremonyCompletion in render-loop
  • render-meter.ts: streakPulseAlpha field on RenderMeterState, drawConfirmingBorder uses pulsing alpha + shadowBlur glow when > 0.7
  • 35 new tests (18 animation-slice ceremony, 15 validation-ceremony, 2 token), 908 total passing across 60 suites, zero TypeScript errors

Story 7.5: Keyboard Navigation & Reduced Motion -- DONE

  • Created src/gameboard/interaction/keyboard-focus.ts: module-level singleton tracking focusTarget (node/port/connection-point/wire) and focusVisible flag, computeTabOrder() (sorted by row/col, expanded node splices ports/wires, connection points appended), computeValidWiringTargets(), advanceFocus() with wrap-around
  • Created src/gameboard/interaction/keyboard-handler.ts: pure getKeyboardAction() + executeKeyboardAction() pattern (matching escape-handler), handles Tab/Shift+Tab, arrow keys, Enter (context-dependent: enter-node, open-params, start-wiring, complete-wiring, place-node), Delete/Backspace, N/Space, Ctrl+Z/Ctrl+Shift+Z
  • Added keyboard-wiring interaction mode to interaction-slice.ts with startKeyboardWiring, cycleWiringTarget, cancelKeyboardWiring, keyboardGhostPosition, setKeyboardGhostPosition actions
  • Created src/gameboard/canvas/render-focus.ts: dashed focus ring for nodes (roundRect 5px offset), ports (circle), connection points (circle), wires (dashed polyline path); wiring target highlights (0.3-alpha for all, 1.0 + wire preview for active)
  • Updated GameboardCanvas.tsx: inline handleKeyDown replaced with keyboard-handler dispatch, mouse move hides focus ring, canvas has tabIndex={0} + aria-label="Gameboard", keyboard-wiring cursor
  • Updated render-loop.ts: focus ring drawn after nodes before wire preview, reduced motion static streakPulseAlpha (0.8)
  • Updated render-placement-ghost.ts: keyboardGhostPosition takes priority over mouse snap, clamped to playable area
  • Updated escape-handler.ts: keyboard-wiring added to cancel-wiring priority, cancelKeyboardWiring on EscapeHandlerState interface
  • Reduced motion audit: render-needle.ts (shadowBlur 0), render-waveform-channel.ts (uniform alpha 0.8), render-meter.ts (static globalAlpha 0.8, shadowBlur 0 in confirming border)
  • 68 new tests (15 keyboard-focus, 40 keyboard-handler, 9 render-focus, 4 existing escape-handler adapted), 976 total passing across 66 suites, zero TypeScript errors

Epic 7 Complete. All redesign epics (5, 6, 7) are done.


Creative Mode Implementation -- DONE

Added a "Creative Mode" sandbox that bypasses puzzle progression:

  • Created src/store/slices/creative-slice.ts: isCreativeMode flag, creativeWaveforms tuple (3 WaveformDefs), enterCreativeMode, exitCreativeMode, setCreativeWaveform, setCreativeWaveformShape actions
  • Added waveform-selector overlay type to overlay-slice.ts
  • Created src/ui/overlays/WaveformSelectorOverlay.tsx: modal with 10 waveform shape options (sine, square, triangle, sawtooth, constant, 5 rectified variants), mini SVG preview icons
  • Extended hitTestMeter() in hit-testing.ts: detects clicks on meter waveform channel area (~59% left side of meter)
  • Updated GameboardCanvas.tsx: in creative mode, clicking input meter waveform opens waveform selector
  • Updated simulation-controller.ts: connection-input nodes use creativeWaveforms when isCreativeMode, meter recording uses creative waveforms, validateTick() skips validation in creative mode
  • Updated App.tsx: starts in creative mode with 6 active meters (3 inputs, 3 outputs), simulation auto-starts
  • Updated palette-items.ts: isCreativeMode parameter unlocks all puzzle nodes regardless of completedLevels
  • 10 new tests for creative-slice, 986 total passing across 64 suites, zero TypeScript errors

Features:

  • All 6 meters active on startup
  • All puzzle nodes available in palette (if any exist)
  • No victory condition -- validation skipped
  • Click input meter waveform to select different waveform shape
  • Output meters are read-only (no selector)
  • Default waveforms: Input 1 = Sine, Input 2 = Square, Input 3 = Triangle

Grid Density Doubling -- DONE

Doubled grid resolution from 32×18 to 64×36 for finer node positioning:

  • GRID_COLS: 32 → 64, GRID_ROWS: 18 → 36
  • Meter zones doubled: left 0-5 (6 cols), playable 6-57 (52 cols), right 58-63 (6 cols)
  • METER_GRID_ROWS: 6 → 12, METER_GRID_COLS: 3 → 6 (same visual size)
  • MIN_CELL_SIZE: 32 → 16 (cells now half the visual size)
  • Node grid sizes unchanged (visually smaller, finer port spacing)
  • All 993 tests passing, zero regressions

Node System Improvements -- DONE

Implemented 5 interconnected changes to the node system:

Phase 1-2: Types & Rotation Utilities

  • Added NodeRotation type (0 | 90 | 180 | 270) to src/shared/types/index.ts
  • Added rotation?: NodeRotation field to NodeState interface
  • Added 'constant' to FundamentalNodeType
  • Created src/shared/grid/rotation.ts: getRotatedPortSide(), getRotatedSize(), getPortApproachDirection(), getPortOffset()
  • 12 new rotation tests

Phase 3-4: Port Positions & Rendering with Body Offset

  • Added NODE_STYLE.BODY_OFFSET = 0.5 constant (half-cell visual offset)
  • Updated getNodeGridSize() to apply rotation when determining dimensions
  • Updated getNodePortPosition() to handle rotated port positions
  • Added getNodeBodyPixelRect() for offset body rendering
  • Updated render-nodes.ts, render-placement-ghost.ts, hit-testing.ts for body offset

Phase 5-6: Dragging Mode & Event Handling

  • Added dragging-node mode to InteractionMode with offset tracking
  • Added rotatePlacement() action to cycle rotation 0→90→180→270→0
  • Added moveNode() action to gameboard-slice.ts
  • Added drag detection to GameboardCanvas.tsx (5px threshold or 150ms delay)
  • Added 'R' key handler for rotation during placement/drag

Routing Updates for Rotation

  • Updated auto-router.ts: getPortGridAnchor() now uses rotation to determine anchor positions
  • Added getPortWireDirection() and portSideToWireDirection() for direction-aware routing
  • Updated findPath() to accept custom start/end directions (instead of hardcoded East)
  • Updated routing-slice.ts to pass correct directions based on node rotations
  • 23 new routing tests

Result: Nodes can be rotated with R key during placement/drag. Ports move to correct sides based on rotation. Wire routing adapts to rotated port positions. All 1115 tests passing.


Quick Reference

What Where
Redesign architecture _bmad-output/planning-artifacts/architecture.md
UX spec (drives redesign) _bmad-output/planning-artifacts/ux-design-specification.md
Original architecture _bmad-output/game-architecture.md
Epics & Stories _bmad-output/planning-artifacts/epics.md
Readiness report _bmad-output/planning-artifacts/implementation-readiness-report.md
Sprint status _bmad-output/implementation-artifacts/sprint-status.yaml
Interactive demos _bmad-output/planning-artifacts/architecture-explorations.html
Story 5.1 (Grid/Viewport) _bmad-output/implementation-artifacts/5-1-grid-coordinate-system-viewport.md
Story 5.2 (Tokens/Themes) _bmad-output/implementation-artifacts/5-2-design-token-system-dual-themes.md
Story 5.3 (Type Migrations) _bmad-output/implementation-artifacts/5-3-core-type-migrations-connection-point-configuration.md
Story 5.4 (Render Refactor) _bmad-output/implementation-artifacts/5-4-render-loop-draw-function-refactor.md
Story 5.5 (Grid Zones) _bmad-output/implementation-artifacts/5-5-grid-zone-background-rendering.md
Story 6.1 (Analog Meters) src/gameboard/meters/ (9 files)
Story 6.2 (Auto-Routing) src/shared/routing/ (3 files) + src/store/slices/routing-slice.ts
Story 6.3 (Wire Signal Rendering) src/gameboard/canvas/render-wires.ts + render-wires.test.ts
Story 6.4 (Node Rendering) src/gameboard/canvas/render-nodes.ts + tests
Story 7.1 (Overlay/Focus) src/store/slices/overlay-slice.ts + src/gameboard/interaction/
Story 7.2 (Palette/Popover/Menu) src/ui/overlays/ (13 files) + src/gameboard/canvas/render-placement-ghost.ts
Story 7.3 (Lid Animation) src/store/slices/animation-slice.ts + src/gameboard/animation/
Story 7.4 (Ceremony Animation) src/gameboard/animation/validation-ceremony.ts + animation-slice ceremony state
Story 7.5 (Keyboard/a11y) src/gameboard/interaction/keyboard-focus.ts + keyboard-handler.ts + src/gameboard/canvas/render-focus.ts
Creative Mode src/store/slices/creative-slice.ts + src/ui/overlays/WaveformSelectorOverlay.tsx
Node System (rotation/drag) src/shared/grid/rotation.ts + src/shared/routing/auto-router.ts + interaction-slice.ts
Test suite npm test (1115 tests, 74 suites)
Playtest plan _bmad-output/playtest-plan.md

Creative Mode Puzzle Authoring System -- DONE

Implemented full puzzle authoring workflow allowing users to create and play custom puzzles:

Phase 1: Start Screen & Mode Selection

  • Created src/ui/screens/StartScreen.tsx: Full-screen entry with "Level Select" and "Creative Mode" buttons
  • Added 'start-screen', 'level-select', 'trim-dialog', 'save-puzzle-dialog' to ActiveOverlay union
  • Created src/ui/overlays/LevelSelectOverlay.tsx: Shows built-in puzzles + custom puzzles section

Phase 2: Off Option for Meters

  • Extended CreativeSlotState.direction to support 'off' (hidden meter)
  • Added "Off (hidden)" option to WaveformSelectorOverlay with X icon
  • Updated gameboard-slice.ts with addCreativeSlotNode action for off→active transitions
  • Meter visibility updates when slot direction changes

Phase 3: Continuous Output Buffering

  • Created src/store/slices/authoring-slice.ts: OutputRingBuffer class (480 samples ~30 sec), authoringPhase state machine (idle→trimming→saving)
  • Updated simulation-controller.ts to push output samples to authoring buffers in creative mode
  • Added "Save as Puzzle" button to SimulationControls (creative mode only)

Phase 4: Trim & Save Dialogs

  • Created src/ui/overlays/TrimDialog.tsx: Canvas-based waveform display with draggable start/end handles for loop selection
  • Created src/ui/overlays/SavePuzzleDialog.tsx: Title/description input with puzzle configuration summary

Phase 5: Custom Puzzle Storage

  • Created src/store/slices/custom-puzzle-slice.ts: CustomPuzzle type with slots, targetSamples, serialized nodes/wires
  • Created src/store/custom-puzzle-persistence.ts: Separate localStorage key, auto-save on changes
  • Added 'samples' shape to WaveformShape union for recorded waveform playback
  • Updated waveform-generators.ts to handle samples shape (looping playback)

Phase 6: Level Select & Puzzle Mode

  • Implemented loadCustomPuzzle() with full gameboard/meter setup
  • Custom puzzles appear in LevelSelect under "Custom Puzzles" section
  • WaveformSelectorOverlay closes immediately in puzzle mode (no changes allowed)

Workflow:

  1. Launch → Start Screen with "Creative Mode" / "Level Select"
  2. Creative Mode: Configure inputs (waveforms) and outputs, build nodes, let simulation run
  3. Click "Save as Puzzle" → Trim dialog with last ~30 sec of output buffered
  4. Drag handles to select loop region → Continue
  5. Enter title/description → Save
  6. Return to Level Select → Custom puzzle appears under "Custom Puzzles"
  7. Load custom puzzle → Same mechanics as built-in puzzles

Files Created:

  • src/ui/screens/StartScreen.tsx + CSS module
  • src/ui/overlays/LevelSelectOverlay.tsx + CSS module
  • src/ui/overlays/TrimDialog.tsx + CSS module
  • src/ui/overlays/SavePuzzleDialog.tsx + CSS module
  • src/store/slices/authoring-slice.ts
  • src/store/slices/custom-puzzle-slice.ts
  • src/store/custom-puzzle-persistence.ts

Files Modified:

  • src/App.tsx: Start screen integration, new overlay imports
  • src/store/index.ts: Added authoring/custom-puzzle slices
  • src/store/slices/overlay-slice.ts: 4 new overlay types
  • src/store/slices/creative-slice.ts: 'off' direction support
  • src/store/slices/gameboard-slice.ts: addCreativeSlotNode action
  • src/ui/overlays/WaveformSelectorOverlay.tsx: Off option, puzzle mode guard
  • src/ui/controls/SimulationControls.tsx: "Save as Puzzle" button
  • src/simulation/simulation-controller.ts: Output buffer recording
  • src/puzzle/types.ts: 'samples' WaveformShape
  • src/puzzle/waveform-generators.ts: samples shape handling

All 1115 tests passing, zero TypeScript errors.