21 Commits

Author SHA1 Message Date
Josh Creek 52cc478b38 docs: record the assertion-first debugging habit, and refresh CLAUDE.md
Adds the lesson this branch paid for repeatedly: the expensive failures
were not broken behaviour but assertions that could not distinguish the
two states they implicitly claimed to, each reporting its own ambiguity
as a confident verdict about the system under test. Waiting on a Fleet
field that does not exist, treating not-yet-started as exited, a p99
that conflated regression with scheduler noise, a validator reading a
response shape Agones never sends, and a build guard that verified stale
code. Five in one session, several costing multiple CI round trips.

Two habits go with it, both of which beat reading code every time they
were tried: make the script report what it saw before theorising about
why, and verify the diagnostics actually fire -- two dumps were added
here and neither ran, one suppressed by a reachability guard and one by
an ERR trap that cannot fire inside functions without errtrace.

Also fixes two stale claims and one gap. Audio is no longer "none at
all"; a procedural AudioManager covers UI, countdown, impact, goal and
engine cues, and only authored assets remain. Five docs/ contracts that
server/security asserts against the manifests were unlisted. And the Go
control plane -- a third of the codebase and the current focus -- had no
structural description at all, so it now gets one: package layout, which
binary is test-only, and the three things easiest to get wrong
(integration tests hidden behind a build tag, start-time config, the
versioned wire contract).

TODO.md's entry now points at its ordered backlog rather than describing
it as deferred non-multiplayer work.

Every factual claim in the new section was checked against the tree.
2026-09-05 23:10:06 +01:00
Josh Creek 8b9ae35b43 test(domain): guard the ranked arena list against Godot registry drift
Task 8.20. `arena_registry.gd` is the documented single source of truth
for arenas, but `domain/ranked.go` keeps a hand-maintained mirror of its
floor-goal entries and nothing checked the two against each other --
ranked_test.go asserts the same three paths the production code
hardcodes, so both could drift together silently.

Drift is not hypothetical in either direction. The registry's own comment
anticipates flipping an elevated variant to random:true once a checkpoint
trained on that geometry is promoted, which ranked would then keep
excluding indefinitely. A rename or removal is worse: the allocator would
hand out a scene path that no longer exists, and the ranked server fails
to load its arena at match start -- after allocation, so it burns a real
match and a real server.

Keeping the two copies is deliberate rather than a wart: ranked arena
selection is server-authoritative and happens before any Godot process
exists. So this guards the relationship instead of removing it, the same
way the golden join-authorisation token guards the signing format. It
parses the registry and fails if the sets disagree either way, if
rotation order diverges from declaration order, or if a ranked path has
no scene behind it. The parser asserts it found both eligible and
ineligible entries, so a format change cannot make everything pass
vacuously. Verified against four drift scenarios.

The allocation-wiring half of 8.20 turned out to be already complete end
to end, with coverage at each hop; recorded in the task row rather than
rebuilt.

Add a Server Unit Tests workflow, because none of this would otherwise
run: the only Go tests CI executed were multiplayer-load's two load
tests, so ~24k lines of control plane gated nothing. Docker-free so it
can gate every push, and it vets the integration-tagged files too, since
those are excluded from the default build and could otherwise rot
uncompiled.

CLAUDE.md's CI section claimed two workflows and no unit-test job; there
were seven and now eight.
2026-09-05 15:05:49 +01:00
Josh Creek ccf7d0fbfe docs: record the closed root blocker and the new probe/rotation contract
docs/MATCHMAKING.md is the stated source of truth for this design, so it
changes first: the probe challenge endpoint and why probing gates
matching rather than merely improving it, and the key-ID rotation
procedure that makes overlapping-key rotation concrete.

multiplayer-next.md §0's root blocker is closed rather than deleted --
what it was, why it blocked everything, and how it was resolved, since
the reasoning is what a future reader needs. Tasks 7.6, 8.15 and 8.31
updated to what actually remains, which in every case is now external
rather than unbuilt.

TODO.md #14 asked for a join-signing design decision; that decision is
recorded with its rationale. CLAUDE.md no longer says a real deployment
cannot complete a match end to end.
2026-09-05 11:00:50 +01:00
Josh Creek b43ad207c1 docs(multiplayer): split spec into MULTIPLAYER_SPEC.md, trim task doc to outstanding work
multiplayer-next.md was a 1662-line mix of standing architecture spec
and task-completion tracking, most of which was dense per-task DONE
evidence for finished Phases 0-6. Split it:

- MULTIPLAYER_SPEC.md (new): the locked architecture decisions, wire
  format, server-side input handling, prediction/reconciliation,
  latency/frame-rate budget, and match lifecycle state machine -
  standing design reference, not task-tracked.
- multiplayer-next.md (trimmed 1662 -> ~370 lines): only outstanding
  work remains - §0 status, §7 Phase 7/8 task tables condensed to
  "what's left" per task, §8-11 reference material (refactoring notes,
  gotchas, testing, flagged items). Phases 0-6 collapsed to a pointer
  at git history instead of ~500 lines of DONE evidence.

Also:
- Repointed every `multiplayer-next.md §N` code comment (N 1-6) across
  Game/scripts, Game/tools and Game/tests to MULTIPLAYER_SPEC.md, since
  those sections moved. Task-number references (`task N.N`, §7-11)
  correctly still point at multiplayer-next.md.
- Updated CLAUDE.md's doc index and docs/TECH_STACK.md's spec-section
  citations to match.
- TODO.md: added a "what's left to actually finish multiplayer
  (human-actionable)" checklist pulled from multiplayer-next.md §0 and
  docs/MATCHMAKING.md - things that need a person (hardware, a design
  decision, a Steam App ID, hands on a controller), not more agent code.
2026-09-04 22:43:13 +01:00
Josh Creek de263f30e8 docs: explain why the matchmaking control plane is Go, fix stale status
Add a Go-vs-C#/Rust/C++ rationale for the matchmaking control plane to
TECH_STACK.md, and point at it from MATCHMAKING.md and README.md.

Also correct CLAUDE.md and README.md, which still described the backend
as unstarted/not built even though server/ has ~13k lines of Go across
matcher, allocator, api, store, security, supervisor and agones.
2026-09-04 19:10:09 +01:00
Josh Creek 4fb7ddfecf docs(multiplayer): consolidate tracking into one document
multiplayer-todo.md and multiplayer-next.md tracked overlapping
information in two places. Fold everything into multiplayer-next.md
(architecture decisions, wire format, task breakdown with checkboxes,
gotchas list, testing notes) and delete multiplayer-todo.md. Section
numbers are unchanged, so existing code comments citing them by
section/task number still resolve; update every such reference to
point at the new filename.
2026-09-01 12:32:43 +01:00
Josh Creek bcc12aad19 docs(multiplayer-todo): add Phase 8 task breakdown for matchmaking and autoscaling
multiplayer-next.md carried the Phase 8 checklist but not the numbered
tasks with acceptance criteria that work actually gets picked up from.
That format lives in multiplayer-todo.md section 7, which already hosts
Phase 7 as in-progress, so Phase 8 goes there too.

Tasks 8.1-8.20 across four groups: backend service (identity, rating
store, Glicko-2, queue), server orchestration and autoscaling, playlists
and client UI, and keeping Docker/CI green. Section 0's short list gains
an index entry, and the status header now says Phase 8 is a 1.0 launch
blocker and the first phase to add a component outside the Godot
project.

Three entries are measured findings rather than plans, each of which
would break a naive implementation: stdout block-buffering making a
log-grep readiness probe hang forever, the hardcoded 7777/udp port
preventing more than one match per host, and compose.phase6-smoke.yml's
dependence on the exact behaviour allocation work would change.

Also fixes a now-false cross-reference: a Phase 4 note read 'not
Phase 8' meaning 'not a later phase', written when no Phase 8 existed.
CLAUDE.md's 'never add new work to multiplayer-todo.md' rule gains the
new-phase exception it always had in practice - Phase 7 was already
there.
2026-08-31 18:49:59 +01:00
Josh Creek 3aa0f5b9c2 docs: scope casual and ranked matchmaking as a 1.0 launch blocker
Queued matchmaking had never been considered anywhere in the planning
docs - not as planned work, and not even on the explicitly-deferred
list. It is a launch requirement, so record the design before code.

Add docs/MATCHMAKING.md covering the model change (community-server ->
per-match allocation), the decision to use Steam for identity and a
project-owned backend for queue/rating/allocation, what the existing
server already provides (--max-matches=1 is the allocation primitive,
ServerConfig, the roster, MatchState), the casual/ranked ruleset split,
and the open questions - rating algorithm, team-to-individual rating,
and the server cost that allocated matches reintroduce.

Ranked is hard-blocked on Phase 7 Steam auth tickets: slot reclaim is
keyed by display name today, and a rating on a spoofable identity is
worse than no rating.

Add Phase 8 to multiplayer-next.md, and correct README/CLAUDE.md/
TECH_STACK.md, which asserted no backend exists or is planned - true
before this was scoped, wrong now.
2026-08-31 18:33:36 +01:00
Josh Creek 964094f65a docs: correct stale multiplayer/C#-backend claims, add TECH_STACK doc
CLAUDE.md and README.md described the pre-multiplayer state (local-only
MVP, planned C# backend) even though server-authoritative multiplayer,
the dedicated server, Docker/CI verification, and Steam transport have
since shipped (Phases 1-6). Update both to reflect reality and add a
docs index in CLAUDE.md pointing at multiplayer-next.md as the current
checklist.

- Add docs/TECH_STACK.md, linked from README, explaining the stack and
  why it's a single GDScript project with no separate backend.
- Add one TODO item for the video settings menu (missing presets/vsync/
  resolution scaling), blocked on the same profiling gate as the
  multiplayer 0.17 tasks.
- Pick up editor-generated .gd.uid sidecars and minor project.godot
  formatting noise from opening the project in Godot 4.7.
2026-08-31 18:20:52 +01:00
Josh Creek 4533da34e0 feat(multiplayer): Phase 1 transport, connection, and lobby
Lands tasks 1.0-1.8 of multiplayer-todo.md: the pure-function test runner,
net_codec (wire format quantizers/pack-unpack), NetworkManager (ENet
transport, manual polling, min-RTT clock sync), MatchNet (handshake,
protocol/tick-rate gating, roster with team+ready state), lobby.tscn (team
columns, switch team, ready toggle), server_boot.tscn (headless dedicated
server with structured logging and an overrun watchdog), and main_menu.gd's
Host/Join-by-IP UI (connecting overlay, cancel, bounded failure path).

Followed by an adversarial review (Opus subagent) that found and fixed two
real bugs - an unvalidated player_name broadcast that let one client's
oversized name head-of-line-block the reliable channel for everyone, and a
server-side roster leak across a host/re-host cycle - plus three gaps in
the test suite itself where a claim of "verified" wasn't actually backed
by what the test checked. All five two-process smoke tests plus the
pure-function suite are green with the strengthened assertions in place.
2026-08-20 08:18:59 +01:00
Josh Creek 04691aaa48 chore(multiplayer): Phase 0 refactors + graphics/perf settings groundwork
Lands the non-networked Phase 0 tasks from multiplayer-todo.md (ship/camera/
arena refactors, sim constants, background FPS handling) plus a first pass
at exposing graphics/performance settings (presets, resolution scaling,
vsync, FPS cap, perf overlay) and a GPU profiling harness for the
real-hardware follow-up in task 0.15b.
2026-08-19 22:37:17 +01:00
Josh Creek 08ab782a9a chore(mcp): disable blender-mcp server telemetry
Upstream's update added telemetry that is on by default. Sets the three
opt-out env vars on the blender server in .mcp.json, which also suppresses
the consent prompt it would otherwise raise through the MCP client.

Documents the addon-side 'Allow Telemetry' preference as a separate switch:
it defaults to on, covers prompts, code snippets, screenshots and trajectory
data, and lives inside Blender where these env vars cannot reach it.
2026-08-16 15:28:15 +01:00
Josh Creek 9fe95ee1b2 docs(mcp): document the scripted blender-mcp addon install
Upstream replaced the manual GUI addon install with a version-aware
`blender-mcp install-addon` CLI, so the agent guide now specifies a
uv sync + install-addon preflight before the first blender-mcp tool
call in a session, and narrows the manual steps to what genuinely
cannot be scripted: enabling the addon, starting the socket server,
and restarting Blender after the addon file is rewritten.

Also fixes the post-clone setup line, which gave `git submodule add`
(the command for vendoring it in the first place) instead of
`git submodule update --init --recursive`, and notes the telemetry
opt-out env vars that came with the update.
2026-08-16 15:27:20 +01:00
Josh Creek 884b7799a0 fix(hud): have game mode hand HUD its target ship instead of group lookup
HUDController found its ship via get_first_node_in_group("ship"), a group
that has 2+ members once a match has an AI opponent — it only worked
because the player ship happened to spawn first. spawn_camera_rig now
wires the HUD's ship the same way it already wires the camera rig's
target.
2026-08-04 19:54:55 +01:00
Josh Creek 1af3e0410d chore: vendor blender-mcp as a git submodule alongside godot-mcp
Both mcp/godot-mcp and mcp/blender-mcp were already cloned locally and
registered in .gitmodules/.mcp.json/CLAUDE.md, but neither submodule's
gitlink had actually been committed, so a fresh clone wouldn't pull
either down. Stages the two gitlinks so `git submodule update --init
--recursive` works as documented.
2026-08-03 19:11:13 +01:00
Josh Creek 46c8275523 feat(*): add wall/ceiling surface pull and retune ball-ship materials 2026-07-20 19:33:28 +01:00
Josh Creek 772f98b7fe feat(*): Fix exported-policy action order to gymnasium's sorted-key layout, add wall-contact penalty and stronger ball-touch reward, and wire Spectate to run01 vs run02 2026-07-19 13:21:14 +01:00
Josh Creek 379ef9910e feat(*): Replace the test terrain arena with an enclosed standard-size space-platform arena (shared ArenaBoundary floor/walls/ceiling scene, starfield sky, ball CCD) and derive TrainingMode placement bounds from it, dropping the out-of-bounds reward guard 2026-07-18 20:18:03 +01:00
Josh Creek 85f96eb15e feat(*): Add self-play RL training pipeline with PPO trainer, in-game GDScript policy inference, and bot opponent support in Match mode 2026-07-18 19:32:51 +01:00
Josh Creek 328831df1f refactor(*): Restructure game into reusable Arena/GameMode architecture with controller-driven ships, adding Free Play and Match modes 2026-07-18 15:34:11 +01:00
Josh Creek 2772087123 chore(*): Add Claude support 2026-07-18 14:39:49 +01:00