The client-bot smoke asserted remote_residual_position_p99 < 0.3. Commit
52ee1810 both passed and failed on that assertion, in runs three seconds
apart on identical code: two clients in one run reported 0.324 and 0.187
with matching snapshot counts, scores and slot checks.
A p99 over a few hundred samples is its worst handful, so on a shared CI
host it measures how often the process was descheduled as much as how
well the interpolator tracks. Locally the same test reports p95 0.025m
and p99 0.081-0.149m; CI's p99 runs 2-4x higher on a healthy build,
which is the entire margin the 0.3 bar had.
Assert two bars instead of one. The tight numeric bar moves to p95,
which is stable run to run, and p99 is bounded by the product's own
REMOTE_VISUAL_MAX_OFFSET/REMOTE_VISUAL_MAX_ROTATION_DEGREES: past those
the visual smoother stops absorbing a correction in a single step, so
exceeding them is a real defect rather than a slow runner. Referencing
the constants also means the test follows the product if that tolerance
is ever retuned.
This is a genuine trade, not a free win: a regression that pushed p99
from 0.2 to 0.35 while leaving p95 healthy now passes where it once
failed. That band is exactly where the noise lives -- 0.324 was observed
on a healthy build -- so the old bar could not tell that regression from
a busy runner either, and paid for the ambiguity with false reds.
Both percentiles are printed with their bars so a future failure shows
which one moved.
Checked first whether goal-driven kickoff teleports were polluting the
metric; they are not. Both the ship and ball paths already skip
accumulation across a reset_gen change.
11 KiB
TODO
Items here that need a person — hardware, an external account, a playtest, a
design decision — are also tracked as GitHub issues under the
needs:human
label, prioritised P0-blocker → P3-low. The issue is the status; this
document and multiplayer-next.md remain the detail. Agent-actionable code
tasks are deliberately not filed as issues — they live in
multiplayer-next.md §7.
Deferred work, in rough priority order. The current architecture (ShipAction/ShipController seam, Arena/GameMode split, code-driven spawning, group-tagged ball/goals) was chosen specifically so these bolt on without rework.
AI opponent (reinforcement learning)
The training pipeline is built — see TRAINING.md (self-play PPO via the vendored godot_rl_agents bridge, JSON policy export, in-game GDScript inference, eval ladder). Remaining:
- (#24) Run the generation-5 handling/intercepts/league/teamplay curriculum described in
TRAINING.md; promote later checkpoints asmedium/hardonly after they clear the match and behaviour gates. The orchestrator now requires three independent paired evaluation seeds for each promotion decision; the current Stage 6 league run remains blocked on its recorded regression/telemetry results. - (#25) Extend generation 5's moving aerial-intercept states with wall plays and rebound scenarios after Stage 5 establishes a productive-air-touch baseline. The opt-in wall-play/rebound state generator is now implemented and enabled for the next Stage 6 league command; training evidence is still required.
- Design team-credit rewards and paired 2v2 evaluation before enabling the deferred teamplay stage.
team_touch_credit_weightis zero by default andevaluate.py --team-size=2provides the opt-in paired evaluator; Stage 7 remains disabled pending recorded 2v2 behaviour gates.
Presentation / AAA polish
The largest gap between this and a AAA-feeling product is presentation, not code. Sequenced after the above for pragmatic reasons, but this is the highest impact per hour.
- (#26) Audio — authored sound design remains open. A dependency-free procedural
AudioManagernow provides safe UI/countdown/impact/goal hooks, an engine tone pitched/levelled from local thrust and turbo state plus a rising-edge turbo cue, and is wired into kickoff, goal, ball-contact, and menu events; replace the placeholder tones with authored engine/turbo/impact/wall/goal/crowd/music assets after selecting distributable files and mixing them on real hardware. - (#27) Custom font remains open. A shared real
Themeresource now styles the HUD/menu surfaces; select and bundle a distributable font so the UI no longer relies onThemeDB.fallback_fontat 10-13 px. - (#28) Video settings are implemented; profiling/visual QA remains.
video_settings.gdand the settings menu expose graphics presets, AA, vsync, FPS caps, resolution scaling, glow, and brightness, with preset-gated SDFGI/SSIL/SSAO/shadows. The remaining gate is measuring the preset ladder and image quality on low/mid-tier reference hardware in the live editor; no further control wiring is implied by this TODO.
Multiplayer (long term)
The single tracking document is multiplayer-next.md — architecture decisions, implementation evidence, and the current checklist all in one place. Server-authoritative multiplayer, prediction, ENet dedicated hosting, and the Phase 6 exported-server Docker/CI verification are implemented; the remaining gates are captured there.
Phase 7 begins with optional GodotSteam bootstrap and a transport boundary; direct-IP ENet remains fully supported. Graphics controls are now implemented separately through the preset/vsync/FPS-cap/resolution-scale work described above; the remaining graphics gate is real low/mid-tier hardware profiling and visual QA (see §5.5 in the multiplayer tracker).
Tasks 0.1–0.15, 0.18–0.25, 0.27, 0.29 are done (see the Phase 0 table in multiplayer-next.md for what each one actually changed — several deviated from the original plan for concrete GDScript/Godot reasons recorded inline). Remaining, all blocked on 0.15b (profile, on reference hardware, in the live editor — not done): 0.16 (camera to _process), 0.17/0.17b/0.17c/0.17d (graphics presets, vsync, resolution scaling), 0.26 (bake the arena GI to retire SDFGI — the largest frame-time win available, costs no image quality since the arena is fully static), and 0.28 (physics separate-thread prototype, flagged as the riskiest task in the phase). 0.15b is #21. These need a human at the editor with real hardware to profile and eyeball, not further code changes.
- (#29) Possible v0.2 split-screen: spawn one
ship_camera_rig+ viewport per local player (camera is already outside the ship scene to allow this). Unrelated to online play.
What's left to actually finish multiplayer (human-actionable)
Everything below needs a person — hardware, a design decision, an external account, or hands on a controller — not more code from an agent working alone. Full detail for each is linked; this list exists so nothing falls through the cracks. Ordered roughly as it blocks.
Each item is also a GitHub issue (linked inline), labelled needs:human plus a
P0-blocker…P3-low priority. Close the issue and tick the box together.
- (#14) Join-signing design decided and implemented. Resolved as HMAC-SHA256 over the canonical claim bytes with a key ID inside those bytes: the allocator signs with one named key while allocated servers hold the set of currently-valid keys, so rotation does not invalidate authorisations already issued for in-flight matches.
allocator.Workernow publishes the signed roster after binding, andcmd/allocatorrefuses to start without key material. Rotation procedure is indocs/MATCHMAKING.md§2; seemultiplayer-next.md§8.31. Nothing human-only remains here — live verification is covered by #17. - (#18) Phase 4 playtest at ~100 ms RTT — does the ship/ball feel local, do contact corrections read as bumps or glitches? Every numeric gate is green; this is a feel judgment no metric can answer.
multiplayer-next.md§0, gate A. - (#19) Phase 5 3v3 gate — a full 6-player match start to finish, with a mid-match disconnect and a late joiner. Only verified so far at 1v1 plus a two-bot CI match.
multiplayer-next.md§0, gate B. - (#20) Phase 6 external gate — run the exported Docker server and clients from separate real machines over the internet, then play a full match (controlled test only, since defect C below is still open).
multiplayer-next.md§0. - (#15) Acquire a project-owned Steamworks App ID and coordinate with Valve — hard prerequisite for Phase 7 (browser, verified tickets, bans, production credentials, ticketed Hosted Dedicated Server SDR) and therefore for Phase 8.
multiplayer-next.md§0, Phase 7;STEAM.md. - (#16) Supply custom GodotSteam client/server build templates and pin them in
steam-dependencies.lock.json(COSMIC_CLASH_STEAM_CLIENT_GODOT/COSMIC_CLASH_STEAM_SERVER_GODOT) —make verify-steam-templatesrefuses a stock Godot binary until these exist.STEAM.md. - (#21) Reference-hardware profiling (task 0.15b) in the live editor on real low/mid-tier hardware — blocks 0.16, 0.17/0.17b/0.17c/0.17d, 0.26 (arena GI bake), and 0.28 (physics separate-thread prototype). Covered above; listed again here because it also gates Phase 5.5's graphics QA gate for multiplayer sign-off.
- (#17) Stand up the live Kubernetes cluster and Agones deployment for Phase 8 — provider-portable manifests exist, but nothing has run against a real cluster; needs the provider-specific deployment overlay (network, DNS, secrets) per
docs/MATCHMAKING.md. - (#31) Build, push and pin the container images the Kubernetes manifests reference. Every image target builds, but no workflow publishes any of them and all manifest digests are still all-zero placeholders, so
deploy/k8s/basecannot pull running images. Needs a registry namespace, publish credentials, and a signing/provenance decision; once digests are real, turn on--require-concreteinmake verify-supply-chainso a placeholder can no longer pass. Blocks #17. - (#33) Move game servers to their own namespace so
cosmic-clashcan enforcerestrictedagain. Agones' Dynamic port policy needs ahostPort, whichbaseline/restrictedforbid, so the whole namespace dropped toprivileged— including the control plane, which mounts the database DSN, workload secret and Steam publisher key. Deferred until the Agones gate was green so a new failure could not be ambiguous. - (no issue — agent-actionable) Phase 8.48 has its own Compose smoke fixture.
compose.allocated-smoke.ymlandscripts/verify_allocated_compose.share independent ofcompose.phase6-smoke.yml— the script states so explicitly and reuses none of its ports — so the allocated-mode flow no longer inherits that fixture's hardcoded port, first-come slots or--max-matches=2. Exercised bymake verify-allocated-compose. - (#22) Release-evidence and human sign-off gates for Phase 8 production launch — once the above are done, someone needs to actually run and sign off the production-shaped checks
multiplayer-next.md§7 lists as infrastructure/production-dependent.
Defect C (slot reservation keyed on display name alone — real, demonstrated, exploitable during the 30 s disconnect window) is not its own action item: it is fixed for free by the Steam auth tickets in task 7.4 above, so nothing to do until Steam identity lands.