fix(multiplayer): align resync recovery targets

This commit is contained in:
Josh Creek
2026-09-01 22:16:50 +01:00
parent cfc82bcea5
commit 1f05f6d524
3 changed files with 18 additions and 2 deletions
+11 -2
View File
@@ -186,6 +186,14 @@ func recover_proposal(proposal_id: String) -> Error:
return _start_request("proposal_recover", HTTPClient.METHOD_GET, "/v1/proposals/" + proposal_id, {}, "")
static func resync_target(resource_id: String, ticket_id: String, proposal_id: String, proposal_open: bool) -> String:
if resource_id == ticket_id and not ticket_id.is_empty():
return ticket_id
if resource_id == proposal_id and not proposal_id.is_empty():
return proposal_id if proposal_open else ticket_id
return ""
func fetch_ranked_profile() -> Error:
return _start_request("ranked_profile", HTTPClient.METHOD_GET, "/v1/profile/ranked", {}, "")
@@ -492,9 +500,10 @@ func _run_pending_resync() -> void:
func _run_resync(resource_id: String) -> void:
if resource_id == state.ticket_id and not state.ticket_id.is_empty():
var target := resync_target(resource_id, state.ticket_id, state.proposal_id, state.has_open_proposal())
if target == state.ticket_id and not state.ticket_id.is_empty():
recover_queue(state.ticket_id)
elif resource_id == state.proposal_id and not state.proposal_id.is_empty():
elif target == state.proposal_id and not state.proposal_id.is_empty():
recover_proposal(state.proposal_id)
@@ -85,6 +85,11 @@ func test_websocket_reconnect_defers_recovery_while_http_mutation_is_in_flight()
client.free()
func test_resync_of_terminal_proposal_recovers_the_ticket() -> void:
assert_eq(ControlPlaneClient.resync_target("proposal-terminal-resync", "ticket-terminal-resync", "proposal-terminal-resync", false), "ticket-terminal-resync", "terminal proposal resync targets the requeued ticket")
assert_eq(ControlPlaneClient.resync_target("proposal-terminal-resync", "ticket-terminal-resync", "proposal-terminal-resync", true), "proposal-terminal-resync", "open proposal resync retains the proposal target")
func test_retryable_mutation_policy_only_retries_safe_failures() -> void:
assert_true(ControlPlaneClient.is_retryable_mutation_response(0), "transport failure is retryable")
assert_true(ControlPlaneClient.is_retryable_mutation_response(408), "request timeout is retryable")
+2
View File
@@ -1553,6 +1553,8 @@ Recovery targeting now follows the same boundary: only an `OPEN` proposal is pol
Client queue/proposal expiry and enqueue epoch metadata now fail closed on malformed, negative, or fractional values instead of being silently coerced to zero. Adversarial metadata tests cover string, negative, and fractional timestamps.
All client resync entry points now apply the open-proposal boundary: a terminal proposal always recovers the durable ticket instead of polling the finished proposal. A direct-resync regression test covers this path.
Ticket projections now validate playlist metadata on every update, rejecting unknown values before either phase or playlist state can mutate. An adversarial higher-revision update test covers this boundary.
Client sessions now fail closed at the expiry boundary and proactively clear credentials before reconnects or authenticated requests. Boundary and malformed-expiry tests cover the lifecycle guard.