fix(rust): hold the reconcile until the world is loaded, and never read a refusal as a revert
All checks were successful
PR Checks / client-build (pull_request) Successful in 20s
PR Checks / server-tests (pull_request) Successful in 25s
PR Checks / frozen-manifest (pull_request) Successful in -1m10s

Two defects the phase 13a walk found by restarting the rig mid-run:

- The watch asked core to reconcile the moment a new boot id appeared, which
  is before the game has loaded its save — every crate looked gone and was
  orphaned. It now waits for the plugin's hello to say `worldReady`; an older
  plugin that never says is taken as ready.
- revert() read any 200 as success. On this bridge a refusal is a 200
  carrying world.error (`not-ready` while loading), so every row would have
  been marked reverted with the game still holding every crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-09-24 02:16:03 -05:00
parent fab31f23e8
commit ba636c8939
3 changed files with 59 additions and 3 deletions

View File

@@ -185,7 +185,9 @@ async function refreshOne(server) {
// A restart or a wipe under a running event is the moment core must be told
// to ask what the world still holds (§11.1). Only a CONNECTED plugin's hello
// counts: a board the game left behind says nothing about now.
if (connected) eventWorld.observeServer(server.id, { bootId: frame.bootId, wipeId: frame.wipeId })
if (connected) {
eventWorld.observeServer(server.id, { bootId: frame.bootId, wipeId: frame.wipeId, worldReady: frame.worldReady })
}
} catch (err) {
// A failure here is one server's, and it must not reach `Promise.allSettled`
// as a rejection that hides which one. Log with the id and carry on.