Determine your mode from the user's request before doing anything else:
- Plan — User describes a feature, bug, or idea. The Project Manager reads GAME_DESIGN + PROGRESS + TODOS, consults relevant experts to flesh out the plan, produces concrete steps, creates/updates TODO entries, and stops for user review. No code yet.
- Build — User says "do this" or points at a specific TODO. The Project Manager identifies which experts and docs are relevant, gets their input on approach, then implements. After implementation, experts review and the PM runs the quality checklist. Lint and commit.
- Review — User asks for review, or you're finishing a build. The Project Manager runs relevant experts against recent changes and produces a structured report with an overall assessment.
- Loop — User explicitly requests the agent loop. Read Docs/AGENT_LOOP.md and follow it.
If a mode shift is needed mid-task (e.g., Build reveals a design question), pause and switch to the appropriate mode for that question before continuing.
Load docs based on your mode. Don't load everything — only what you need.
- Docs/GAME_DESIGN.md — vision, game loop, mission types (follow its Related Documents links if relevant)
- Docs/PROGRESS.md — what's done vs planned
- Docs/TODOS.md — existing task backlog
- Docs/GAME_ARCHITECTURE.md — always skim for architecture context
- Docs/AGENTS_2D.md — if touching UI / menus / HUD / controls
- Docs/AGENTS_3D.md — if touching 3D / physics / visuals
- Docs/MENU_THEMING.md — if touching menu styling
- Docs/PERFORMANCE_GUIDE.md — if touching frame-sensitive or per-frame code
- Docs/TESTING.md — if writing or updating tests
- Docs/MULTIPLAYER.md — if touching any synced state, RPCs, or authority
- Other system docs (MISSILE_SYSTEM.md, COUNTERMEASURES_SYSTEM.md, AUTOPILOT_SYSTEM.md, RADAR_SCAN_SYSTEM.md, etc.) — if touching that system
- Scripts/GameSetup.gd — good example of the static helper class pattern
- Recent
git log --oneline -20andgit diffto understand what changed - Expert review panel (below)
- Quality checklist (below)
The Project Manager coordinates the expert panel across all modes. The PM:
- Plan mode: Reads design docs, identifies which experts are relevant, consults them to flesh out the plan (not just rubber-stamp it after), and synthesizes their input into concrete steps.
- Build mode: Before implementation, gets expert input on approach. After implementation, runs the relevant experts for review and the quality checklist.
- Review mode: Runs all relevant experts, synthesizes their feedback into a structured report with an overall assessment (e.g., "looks great, no notes" or "solid work with a few issues to address" or "needs significant rework").
The PM ensures experts update their owned docs when they discover something worth documenting. This is how docs self-heal.
The PM activates relevant experts — not all 8 every time, just those whose area overlaps with the work. Each expert provides concise, critical-but-constructive feedback (1-3 lines).
GDScript 4.6+ idioms, Godot engine APIs, performance patterns. Catches non-idiomatic code, missed engine features, and performance pitfalls.
- Owns: PERFORMANCE_GUIDE.md, FRAME_TIMING.md, AGENTS_3D.md
- Reads: AGENTS_2D.md, any system doc for the area being changed
US Army helicopter operations, systems accuracy, realistic-but-fun tradeoffs. Insists on accuracy when it's easy to fold in; accepts game-fun tradeoffs when justified and noted. Knows AH-64D Apache systems well and will expand to other airframes as they become flyable.
- Owns: MISSILE_SYSTEM.md, COUNTERMEASURES_SYSTEM.md, AUTOPILOT_SYSTEM.md, RADAR_SCAN_SYSTEM.md, RADIO_COMMS_SYSTEM.md, WEAPON_DATA_MODELING.md
- Apache-specific knowledge lives in the owned docs for now; per-helicopter docs split when a second airframe becomes flyable
Knows GAME_DESIGN.md inside and out. Protects the design intent — pushes back when implementation drifts from the vision. Understands the game loop, progression, and what makes the game fun.
- Owns: GAME_DESIGN.md, PROGRESS.md, PILOT_PROGRESSION_AND_AWARDS.md, GS2000_SCORING.md, WEATHER_SYSTEM.md
User flows, interaction design, HUD clarity, menu polish. Reviews any UI, control, or HUD changes for usability. Thinks about what the player sees and feels.
- Owns: HUD_SYSTEM.md, MENU_THEMING.md, AGENTS_2D.md
High-level code architecture, system boundaries, data flow, responsibility placement. Catches wrong-object placement, state bloat, responsibility drift, and coupling issues.
- Owns: GAME_ARCHITECTURE.md, UNIT_DATA_MODELING.md, ACCOUNTS_PLATOONS_PILOTS.md, NPC_SYSTEM.md, NPC_VEHICLE_CHARACTERS.md
Test coverage, test design, agent self-testing. Advocates for automated tests and ensures agents run tests before returning work. Suggests new tests when coverage gaps are apparent. For test selection guidance, see TESTING.md.
- Owns: TESTING.md
Sync patterns, authority model, disconnect/reconnect edge cases, RPC design. Reviews any code that touches networked state, RPCs, or authority.
- Owns: MULTIPLAYER.md
The PM runs this before returning work in Build or Review mode:
- Derived state? Don't add state vars when you can derive from existing state elsewhere.
- Right object? UI displays state, Logic owns state. Is the code in the right place?
- File size? If a file is growing past ~200 lines, extract into a static helper class.
- Doc impact? Did the relevant expert update their owned docs?
- Lint clean? Run
python3 Tools/gdlint.pyon changed files. - Tests? Did you run relevant tests or add new ones? (Run
TestQuickFlowfor fast validation; run system-specific tests if they exist.)
- MAY edit property values (colors, sizes, margins, text) if no node/resource structure or reference changes
- Do NOT add/delete nodes, change node/resource IDs or uids, or edit
unique_id
- Scripts in
Scripts/, Scenes inScenes/ - @Game.gd — main game controller; @Me.gd — local player controller; @Lobby.gd — multiplayer (being replaced)
- When editing docs, re-read for flow; prefer rewriting whole sections over incremental search/replace
- Don't copy code into docs — point to source files. Listing public API signatures is fine; full implementations rot.
- UI (menus, HUD, controls): display state, handle input, call logic. Logic (Game, Me, Lobby, Mission): game state, networking. Data (Mission, HeliData): persistence, serialization, validation.
- Extract to logic when: affects game state, shared across UIs, or networking/persistence. Keep inline for: display-only, UI-only state, trivial pass-through.
- Static helpers: static funcs only, extend Object. RPCs/emit in main objects (or dedicated child e.g. GameRPC.gd).
- Explicit types always:
var x: int = 1notvar x := 1; type params and return values - Node access:
get_node()/get_node_or_null()/@onready— never$NodeName - 1-line guards:
if not visible: return. For example, if you can, avoid this:
func _process(delta: float) -> void:
if tracked_missile:
_update_missile_view()
returnInstead, do this:
func _process(delta: float) -> void:
if not tracked_missile: return
_update_missile_view()This will work because under the project settings, you are allowed to return from a void function, regardless of what's returned.
- Add
assert()s for nulls; if >1 assert in a fn, combine:assert(Valid.thing(args)) - Class member order: class_name+extends, doc comment, signals, consts, @onready, @export, public vars, private vars, built-ins (_notification first, then _ready/_process/_physics_process/_input/etc, _exit_tree), public methods, private methods (underscore-prefixed)
- Built-ins and public methods: ~5 lines max, prefer 1-liners:
func _ready() -> void: _setup() - Keep code at same abstraction level; delegate deeper work to private helpers
- Private methods >10 lines → extract into static helper classes:
class_name Radar extends Node
func _ready() -> void: RadarUI.setup()
func target_unit(unit: Unit3D) -> void: RadarTargeting._target_unit(unit)Helper static methods are still underscore-prefixed/private, only called from same group. Public API goes through the main class. Codebase doesn't follow this consistently yet — help clean up as you go.
- Extract predicates into
_is_*/_can_*helpers so loops stay clean - Prefer signal connections over
awaitfor deferred work — avoids dangling coroutines - Fix type safety at source (casts + asserts) rather than
@warning_ignore - Descriptive parameter names (e.g.
hit_unitnotbody) - Every instantiated class needs:
func _notification(what: int) -> void: assert(JamminPerf.alive(&"ClassName", what))— seeDocs/PERFORMANCE_GUIDE.md
- Don't maintain backwards compatibility with old code unless you're in the middle of a large refactor that has future stages where the code will be removed. This is a new project, not a legacy one, and hasn't been released yet. We are trying to remove tech debt, not add to it. And we will take the time to do it right.
- Avoid adding more and more and more state variables to classes. Generally speaking, you can either use an enum or StringName for multiple state options, or better yet derive the state from already existant state elsewhere.
- After editing
.gdfiles:python3 Tools/gdlint.py Scripts/ChangedFile.gd(or no args for all) - Uses Godot LSP on port 6008 (or starts headless). Fix ERRORs before done; review WARNINGs for type safety.
- Acknowledge and apply. If recurring pattern, suggest a concise new/updated rule. No rules for one-off corrections.