8 Claude Code sessions mapped to 4 phases. Each session is ~3–4 hours of focused work. Sessions are designed to produce a playable build at the end of each phase. M3 (end of Session 4) is the critical validation gate — if the decay/restoration loop doesn't feel compelling, pause and iterate before investing in Phase 3+.
Total estimated build time: 6–8 weeks at 10–15 hrs/week.
Scope:
- Create Godot 4.4 project with full folder structure (see CLAUDE.md)
- Configure GDScript strict mode in project settings
- Build Player scene: CharacterBody3D → Camera3D → RayCast3D
- WASD movement: 4.0 m/s walk, 6.5 m/s sprint (Shift)
- Mouse look: sensitivity 0.002, invert-Y support
- Head bob: sinusoidal Y-axis, amplitude 0.03m, frequency matched to step speed, toggleable
- No jump. Gravity + floor snapping.
- Build test room: 6m×6m×3m using CSGBox3D for walls/floor/ceiling
- OmniLight3D (warm, 4500K)
- One StaticBody3D pedestal (CylinderMesh, 0.5m radius, 1m height)
- Create autoload stubs:
srs_engine.gd,decay_manager.gd,save_manager.gd,palace_manager.gd- Register all 4 in Project Settings → Autoload
- Each prints initialization confirmation in
_ready()
- Wire RayCast3D (3m forward from camera) + InteractionManager.gd
_physics_process(): checkraycast.is_colliding(), print collider name
Deliverables:
- Runnable project with smooth FPS movement in a lit room
- RayCast detects pedestal on approach
Verification:
- Walk speed feels appropriate (not too fast, not sluggish)
- Mouse look is responsive with no drift
- Head bob is subtle (disable and compare — it should enhance, not nauseate)
- Can't walk through walls
- Console prints pedestal name on approach
Context files for this session:
- CLAUDE.md (full)
- This section of IMPLEMENTATION-ROADMAP.md
Scope:
- Implement
srs_engine.gdfully:- Data: Dictionary[String, MemoryObjectData] keyed by object ID
create_object(id: String, front: String, back: String, category: String) → Dictionaryreview(id: String, remembered: bool) → voidget_decay(id: String) → float(returns 0.0–1.0)get_due_objects() → Array[String]- SM-2 logic: correct → interval progression (1d → 6d → interval × ease). Incorrect → reset to 1d, ease -= 0.2 (min 1.3)
- Build SRS unit test scene (
res://test/test_srs.tscn):- Create 10 objects programmatically
- Review with known inputs (mix of remembered/forgot)
- Assert expected intervals and decay values
- Print PASS/FAIL to console
- Build SubViewport-based 3D panel prototype:
- SubViewport (512×384) with Control scene: VBoxContainer → Label + TextEdit + Button
- Render via ViewportTexture on QuadMesh (MeshInstance3D)
- Position 0.5m above pedestal, billboard mode
- Test keyboard capture: can type in TextEdit while panel is open
- Test readability at 1m, 1.5m, 2m distances
Deliverables:
- SRSEngine autoload with working SM-2 and passing unit tests
- Visible 3D panel that captures text input
Verification:
- Unit test prints all PASS
- Panel text is readable at interaction range (1.5m)
- Keyboard input goes to panel, not player movement, when panel is open
Context files:
- CLAUDE.md
player.gd(from Session 1)srs_engine.gd(building in this session)- This section of IMPLEMENTATION-ROADMAP.md
Scope:
- Build
memory_object.tscnscene:- Node3D root → MeshInstance3D (procedural shape) + Label3D (front text)
- Mesh selection:
hash(front_text) % 5maps to [SphereMesh, BoxMesh, PrismMesh, TorusMesh, CylinderMesh] - StandardMaterial3D: albedo_color from category color, emission_enabled with energy 0.3
- Build
creation_panel.tscn:- SubViewport UI: Front (TextEdit, max 100 chars), Back (TextEdit, max 500 chars), Category (LineEdit, max 30 chars), Category Color (preset color buttons), Create (Button), Cancel (Button)
- On Create: call
SRSEngine.create_object(), instancememory_object.tscnat pedestal position, close panel
- Build
review_panel.tscn:- SubViewport UI: Front label (large text), "Reveal" button / E key, Back label (hidden until reveal), "Remembered" (1 key / green button), "Forgot" (2 key / red button)
- On grade: call
SRSEngine.review(), close panel
- Wire full interaction flow in InteractionManager:
- E on empty pedestal → open creation panel
- E on populated pedestal → open review panel
- Mouse look disabled while panel open
- Esc closes any panel
- Label3D: billboard mode, font size 24, opacity scales with distance (1.0 at <3m, 0.0 at >8m via
_process()) - Crosshair: small dot default, changes to ring when RayCast hits interactable
Deliverables:
- Can create memory objects on pedestals via in-world UI
- Can review objects with full front→reveal→grade flow
- Objects are visually distinct by category and term
Verification:
- Create 10 objects across 3 categories. Visual variety is apparent.
- Review cycle takes < 10 seconds
- Labels readable at interaction range, invisible from far away
- No input conflicts (movement doesn't happen during panel typing)
Context files:
- CLAUDE.md
player.gd,interaction_manager.gd,srs_engine.gd- This section of IMPLEMENTATION-ROADMAP.md
Scope:
- Write
room_decay.gdshader:- Uniform:
decay_amount(float, 0.0–1.0) - Uniforms:
base_albedo(sampler2D),crack_overlay(sampler2D),dust_color(vec4) - Effect: lerp albedo toward desaturated version (gray-blue) by
decay_amount * 0.8 - Effect: blend crack_overlay alpha when
decay_amount > 0.3 - Effect: reduce emission energy inversely with decay
- Apply to all room meshes via shared ShaderMaterial
- Uniform:
- Write
object_decay.gdshader:- Same desaturation + crack base as room shader
- Add dissolve: noise texture threshold mask, pixels below
(1.0 - decay_amount)become transparent - Add vertex displacement:
VERTEX += NORMAL * noise * decay_amount * 0.05 - Add emission pulse at low decay:
sin(TIME * 2.0) * 0.2modulation
- Implement
decay_manager.gd:- Every 1.0 second (Timer): iterate all loaded rooms
- Per room: average decay of objects via
SRSEngine.get_decay() - Set room shader
decay_amountparameter - Set OmniLight3D energy:
1.0 - (decay * 0.9)(min 0.1) - Set FogVolume density:
decay * 0.15 - Note: per-room FogVolume instances (not global fog)
- Restoration animation (on successful review):
- Tween: object
decay_amountfrom current → 0.0 over 2.5 seconds - GPUParticles3D: 30 particles, gold color (#FFD700), 1.5s lifetime, burst on review
- Room shader
decay_amountrecalculates (instant, since average changed) - AudioStreamPlayer3D: restoration chime, pitch =
0.6 + (pre_decay * 0.6)(more overdue = lower start pitch sweeping up = more dramatic)
- Tween: object
- Debug time accelerator:
- F5 toggles time scale between 1× and 100×
- HUD label shows "TIME ×100" when active
- Source placeholder textures:
crack_overlay.png: any CC0 crack texture (will be replaced later)noise.png: Godot's built-in NoiseTexture2D exported to PNG, or generate in shader
Deliverables:
- Rooms and objects visually decay along a continuous 0–100 spectrum
- Restoration animation plays on successful review with particle + audio feedback
- F5 time acceleration for rapid testing
Verification:
- F5 → watch room go from pristine to ruined in ~30 seconds. All 5 states visually distinct.
- Review a heavily decayed object → dramatic restoration with golden particles and chime
- Review a barely-decayed object → subtle confirmation
- 60 FPS with 1 room, 16 objects, all shaders active
Context files:
- CLAUDE.md
srs_engine.gd,decay_manager.gd,memory_object.gdroom_decay.gdshader,object_decay.gdshader(creating in this session)- This section of IMPLEMENTATION-ROADMAP.md
Scope:
- Implement
palace_manager.gd:- Grid: Dictionary[Vector2i, String] mapping grid positions to room IDs
place_room(template: String, name: String, grid_pos: Vector2i) → String(returns room ID)remove_room(id: String) → voidget_adjacent_positions(grid_pos: Vector2i) → Array[Vector2i](returns empty adjacent cells)get_room_at(grid_pos: Vector2i) → Dictionary(room data or null)- Atrium always at (0, 0), cannot be removed
- New rooms only placeable adjacent to existing rooms
- Build 5 room template scenes:
- Study (8 pedestals): warm wood tones, desk lamp OmniLights, bookshelves (CSGBox)
- Gallery (12 pedestals): white walls, SpotLight3D per pedestal, open layout
- Workshop (10 pedestals): metal shelves (CSGBox), workbench, warm overhead light
- Garden (6 pedestals): open courtyard feel, stone textures, planters (CSGBox), bright ambient
- Vault (16 pedestals): metal walls, dim blue OmniLight3D, dense grid layout
- Each: NavigationRegion3D with baked NavMesh, Area3D doorway triggers on consistent positions (centered on one wall)
- Build
hallway.tscn:- Straight corridor: 2m wide × 3m tall × 6m long
- Collision on all surfaces
- Door Area3D triggers on each end
- When player enters doorway trigger, signal to load/ensure connected room
- Wire room instancing: PalaceManager.place_room() → instance the PackedScene → position at grid_pos × room_spacing (e.g., grid_pos × Vector3(12, 0, 12)) → generate hallway between connected rooms
Deliverables:
- 5 room templates, each with correct pedestal counts and distinct visual identity
- Rooms instance at grid positions with hallway connectors
- Can walk from atrium through hallway into another room
Verification:
- Place all 5 room types via code. Walk through each. Pedestal counts correct.
- Walk from atrium → hallway → room seamlessly. No collision gaps.
- Place 3 rooms in an L-shape. Hallways connect correctly.
Context files:
- CLAUDE.md
palace_manager.gd(building),player.gd,decay_manager.gd- This section of IMPLEMENTATION-ROADMAP.md
Scope:
- Build
minimap.tscn(CanvasLayer):- Toggle with M key
- Top-down grid view of palace layout
- Room icons (rectangles) colored by average decay (green→yellow→red gradient)
- Room names displayed on icons
- Empty adjacent cells show "+" button for room placement
- Click "+" → template selection dropdown → name TextEdit → confirm button
- Click existing room → fast travel to that room
- Fast travel:
- Click room on minimap → 0.5s AnimationPlayer fade to black → teleport Player to room entrance position → 0.5s fade in
- Disable fast travel while review/creation panels are open
- Room loading optimization:
- Track current room (player is inside) and adjacent rooms (connected via hallways)
- Only keep current + adjacent rooms instanced in scene tree
- On room transition: load new adjacent rooms, free rooms that are now 2+ steps away
- Store freed room state in PalaceManager (pedestal occupancy persists via SRSEngine, only visual instances are freed/re-created)
- Test with 10 rooms:
- Place 10 rooms via minimap
- Fast travel to each
- Walk between adjacent rooms
- Profile FPS
Deliverables:
- Functional minimap with room placement and fast travel
- Room loading/unloading keeps performance stable at 10+ rooms
Verification:
- Place 10 rooms via minimap UI (no code). Layout makes spatial sense.
- Fast travel to any room in < 1.5 seconds. No clipping on arrival.
- Walk through 3 connected rooms. No visible pop-in or loading hitch.
- 60 FPS maintained with 10 rooms placed, 3 loaded, ~80 objects
Context files:
- CLAUDE.md
palace_manager.gd,player.gd, all room template scenes- This section of IMPLEMENTATION-ROADMAP.md
Scope:
- Audio implementation:
- 5 ambient AudioStreamPlayer3D tracks (one per room template mood). Cross-fade on room transition (1.0s linear fade)
- Footsteps: AudioStreamPlayer3D on Player. 3 surface types (wood, stone, metal) × 4 random variations = 12 samples. Random pitch ±10%, volume ±10%. Trigger on step (time-based, matched to walk speed)
- UI sounds: panel_open, panel_close, button_hover, button_click, object_materialize (5 samples)
- Restoration chime: 1 sample, pitch-shifted by pre-review decay
- Ambient volume scales with decay: full at decay 0, -12dB at decay 100
- Source all audio from freesound.org (CC0 license)
- Implement
save_manager.gd:save_game() → bool: serialize PalaceData (all rooms, objects, SRS state, layout) to JSON. Write touser://recall_save_a.json(alternating a/b on each save)load_game() → PalaceData: read most recent valid save file, validate JSON keys, re-instance rooms, re-create objects, restore SRS state- Autosave triggers: on object creation, on review, on room placement, every 300 seconds (Timer)
- Error handling: if both save files are corrupt, start fresh with Atrium only
- Settings menu (
settings_menu.tscn):- Esc → pause (get_tree().paused = true) → settings overlay
- Sliders: mouse sensitivity (0.0005–0.01), FOV (60–90), master volume (0.0–1.0)
- Toggles: head bob (bool), invert-Y (bool)
- Save to
user://recall_settings.jsonon change - Load on startup
Deliverables:
- Immersive audio that responds to room state
- Reliable save/load with corruption protection
- Settings menu with persistent preferences
Verification:
- Walk between rooms. Ambient cross-fades smoothly. Footsteps match surface.
- Create palace with 3 rooms, 15 objects. Save. Quit. Reload. Verify all data matches.
- Change all settings. Quit. Relaunch. Settings persisted.
Context files:
- CLAUDE.md
save_manager.gd(building),palace_manager.gd,srs_engine.gd- This section of IMPLEMENTATION-ROADMAP.md
Scope:
- HUD finalization (
hud.tscn):- Crosshair: 4px white dot center screen. Changes to 12px ring when RayCast hits interactable.
- Room name: top-center label, fades in on room entry (0.3s), holds 3 seconds, fades out (0.5s)
- Due counter: bottom-right, "{N} due" text, updates on review and every 60 seconds
- Minimap toggle hint: bottom-left, "[M] Map" text, subtle
- Performance optimization:
- Run Godot profiler with 20 rooms placed, 200 objects total, 3 rooms loaded
- Targets: 60 FPS, <100ms frame spikes, <500MB RAM
- Optimization levers if needed:
- Shader complexity reduction (remove vertex displacement, simplify noise)
- LOD: reduce object mesh detail at >10m (lower subdivision or switch to billboard)
- Reduce fog particle count
- Reduce audio streams (pool to max 8 simultaneous)
- Bug fix pass: Walk the full palace. Test every interaction. Verify edge cases:
- Create object → immediately review it (decay should be 0)
- Fill all 16 slots in Vault → try to create 17th (should be rejected)
- Fast travel during creation panel (should be blocked)
- Save with 0 objects, 0 rooms (beyond atrium). Load.
- Rapid-fire reviews (review 10 objects in <30 seconds)
- Real content playtest:
- Add 50+ vocabulary items across 3 categories ("Spanish Vocab", "Network+", "GDScript")
- Play for 30 minutes: build rooms, place objects, wait for some decay (use F5 accelerator), review
- Note any friction, confusion, or missing feedback
- Document remaining issues in CLAUDE.md
Deliverables:
- Polished HUD that provides info without cluttering the view
- 60 FPS performance at full MVP scale
- Bug-free 30-minute playtest session
Verification:
- All HUD elements visible, readable, non-overlapping
- Profiler shows consistent 60 FPS at 20-room / 200-object scale
- 30-minute session completes without crashes
- All edge cases handled gracefully
Context files:
- CLAUDE.md (update Current Phase to reflect completion)
hud.gd,player.gd,decay_manager.gd- This section of IMPLEMENTATION-ROADMAP.md
Per-session rule: Include CLAUDE.md + the specific session section from this roadmap + max 5 relevant source files. Do NOT dump the entire project into context.
Key files by session:
| Session | Must Include | May Include |
|---|---|---|
| 1 | CLAUDE.md | — (nothing exists yet) |
| 2 | CLAUDE.md, player.gd | interaction_manager.gd |
| 3 | CLAUDE.md, srs_engine.gd, player.gd | interaction_manager.gd, memory_object.gd |
| 4 | CLAUDE.md, srs_engine.gd, memory_object.gd | decay_manager.gd, room scene file |
| 5 | CLAUDE.md, palace_manager.gd, player.gd | decay_manager.gd |
| 6 | CLAUDE.md, palace_manager.gd | minimap.gd, player.gd |
| 7 | CLAUDE.md, save_manager.gd, srs_engine.gd | palace_manager.gd |
| 8 | CLAUDE.md, hud.gd | player.gd, decay_manager.gd |
After each session: Update the Current Phase section in CLAUDE.md with completed tasks and next steps. This is the primary recovery mechanism if context is lost.