Commit Graph

13 Commits

Author SHA1 Message Date
Josh Creek 801fca7cb0 fix(matchmaking): make regional RTT evidence obtainable end to end
domain.validCandidate hard-requires a non-empty PredictedRTT map, but
CreateQueueTicket persisted an empty one and the only endpoint that
could fill it returned 503 in every real binary, because Service.Probe
was assigned nowhere outside api tests. No client-created ticket could
ever be selected by the matcher. The Godot client had no probe method at
all, so even a wired backend was unreachable from the game.

Four distinct defects had to be fixed for this path to work:

Nothing issued the nonce ProbeProvider was meant to compare against, so
the contract could not be satisfied even in principle. Add
POST /v1/probes/{region}/challenge, backed by a durable single-use
challenge -- durable because any replica may serve the answer for a
challenge another replica issued. RTT is the interval between issuing
and receiving, so no client-reported latency reaches placement.

CreateQueueTicket marshalled a nil map to JSON `null`, a JSONB scalar
rather than an object, and jsonb_set rejects that with "cannot set path
in scalar". RecordProbe would have failed at runtime even once wired.
Persist an object, and normalise non-object values in the update for
rows already written.

A nil ProbeRecorder made the handler report success while persisting
nothing, which silently leaves the ticket unmatchable. That is a
misconfiguration, not a successful probe; it now returns 503.

A successful probe updated PostgreSQL only. The candidate inserted at
enqueue time carries an empty RTT map, and the Redis keyspace has its
TTL continually refreshed, so the stale entry need never repair itself.
Refresh that player's projection after the probe commits.

Client side: add the challenge/answer round trip and have the
matchmaking screen collect evidence before creating a ticket, since
queueing first produces a search that can never match. Probing every
region fully is not required -- placement uses whichever regions
answered -- but queueing with none is refused rather than silently
stalling.

New integration test drives the real enqueue and probe paths and then
asks the actual matcher predicate, rather than hand-building a candidate
the way the unit tests do -- which is exactly why they missed this.

Also make the integration schema reset drop the whole public schema: the
enumerated table list silently broke with each new migration.
2026-09-05 10:49:28 +01:00
Josh Creek 2c648514ba test(server): fix and wire up the two unrun Python suites
test_contracts.py required operation ID `recordPlayerConnected`, but
openapi.json names that endpoint `claimPlayerConnection` — the accurate
name, since POST /servers/{id}/connect claims a connection lease and
returns a generation. Align the test on the document and assert the set
difference, so a future mismatch names the missing operation instead of
reporting "False is not true".

test_observability_manifests.py copied only two of the four files the
checker reads, so it died on a missing kustomization.yaml before ever
reaching the mutated namespace. Copy the full fixture, split the
namespace and scrape-path mutations into separate cases so either
defect produces its own diagnostic, and add an unmutated-copy case so a
broken fixture can't make the mutation cases pass vacuously.

Neither suite was invoked by any Make target or workflow, which is why
both could sit red. Add them, plus test_threat_model.py, to
verify_multiplayer_local.sh.
2026-09-05 10:10:11 +01:00
Josh Creek 3e0022ce9c feat(multiplayer): persist connection generation leases 2026-09-03 13:38:13 +01:00
Josh Creek aa446cfbfe fix(multiplayer): reconcile authoritative initial connections 2026-09-03 00:02:04 +01:00
Josh Creek 55b88a3aa5 fix(multiplayer): converge events through REST 2026-09-02 18:54:10 +01:00
Josh Creek 91658fbc13 fix(multiplayer): recover assignment handoff 2026-09-02 18:47:00 +01:00
Josh Creek ffc7856993 fix(multiplayer): align proposal participant contract 2026-09-01 22:52:52 +01:00
Josh Creek e376e1e80d fix(multiplayer): align websocket assignment ids 2026-09-01 22:42:04 +01:00
Josh Creek 47aa196b59 feat(multiplayer): publish ranked profile contract 2026-09-01 21:51:42 +01:00
Josh Creek cc12260225 feat(multiplayer): expose server shutdown acknowledgement 2026-09-01 16:50:26 +01:00
Josh Creek 467c25b20c feat: expose validated assignment endpoints 2026-09-01 08:17:39 +01:00
Josh Creek 5b8638e15e feat: define matchmaking state transitions 2026-08-31 20:10:51 +01:00
Josh Creek f3e7538fb7 feat: publish matchmaking v1 contracts 2026-08-31 20:09:50 +01:00