0.2.3: Clementia S2 releases a dead, hidden, uncarriable or pathless rider at once (slot and weight back before the same tick's boarding checks); same-browser match recovery never takes the seat of a window that is reloading or still starting (a tab keeps its own session token; seats are stamped at the server's welcome, the older welcome wins); golden: diy-135 moves (outcome unchanged)

This commit is contained in:
sganggs committed 2026-10-10 01:58:06 +08:00
1 parent 821ab827bd
commit ba5b998a72
19 files changed
+580 -60

No files matched your search

+40 -2
View File
@@ -284,9 +284,9 @@ installability check runs in Chrome's ordinary temporary profile. Native OS inst
Reproduced with an idle window retaining its own session while a second match window closes. The title/lobby now
lists local saved match seats using bounded metadata beside the existing recent-token ring. Explicit selection asks
live tabs to claim the chosen token again before adopting it and reloading. An active tab is never deliberately
live tabs to claim the chosen token again before adopting it (same-document reconnect since §28.27). An active tab is never deliberately
replaced; names/IP are not authentication. Missing BroadcastChannel refuses recovery. A server-generated replacement
token or leaving clears obsolete metadata. No server API changes; normal automatic startup selection is preserved.
token or leaving clears obsolete metadata. The initial feature changed no server API; §28.27 later adds optional hello guards and keeps startup on the tab's own identity.
Tests: live tab protection, closed-tab recovery, simultaneous claim arbitration, no channel/unknown id, malformed
storage and expired metadata; a real two-window browser restores the identical server playerId and room after closing
@@ -417,3 +417,41 @@ clear time and end reason. All other 284 scenarios, including the five other gol
The focused checks cover all four tier/elite forms, selectability/control/cooldown boundaries, actual casts,
healing chains and enemy splash. Full CI passes: 5879 tests, 5876 passed, 0 failed, 3 skipped; setup, smoke, lint,
imports and typecheck pass. This round changes no production UI and does not rerun the opt-in browser suites.
### 28.27 Cabin capacity and local recovery races — #431
- **Observed:** 克莱门莎 S2 removed the carry buff from dead, hidden, uncarryable or unreachable passengers but
retained them in the passenger list and weight budget. Two weight-3 riders blocked a replacement after one died;
a dead zero-weight rider still occupied one of the 15 slots; a dead weight-4 rider retained four budget points.
- **Now:** after moving the cabin, release invalid passengers and return each one's current weight before either
boarding limit is checked that tick. Remaining passengers keep their order and movement. This corrects bookkeeping
under the existing S2 rules and official blackboard limits (sources in §28.1); no new gameplay assumption is added.
- **Observed (#431):** a recovery attempt with a smaller tab id could claim a token while its owner was refreshing
or still initializing. The original page then fell back to another recent identity, or erased its own token.
- **Now:** startup with a session token chooses only that token or null and answers ownership queries immediately.
The first server welcome for a token stamps its ownership; reload/reconnect preserves that stamp. Earlier welcome
wins a conflict, a live document wins an equal-stamp duplicate, and tab id resolves remaining ties. Explicit recovery
keeps its choice only in memory and reconnects in the same document; only welcome persists it. An original owner
returning cancels the lower-priority choice, including a later welcomed holder's reconnect. Nicknames and IPs remain
irrelevant to identity; a refused recovery retains the previous nickname until its own welcome.
- **In-flight handshake:** a hello can reach the server after another connection's welcome. Optional `hello.noReplace`
makes shared candidates refuse a currently connected seat; `hello.claimAt` lets the server refuse an established
holder with a later welcome stamp. Refusal is `SESSION_IN_USE`, before rebinding or closing the incumbent socket.
Normal same-stamp reconnects and older clients remain accepted. These are local ownership hints, not authentication;
the bearer token remains the authority. A revoked hello's stale welcome is ignored before going online, flushing
queued actions or notifying listeners, and retries use a new socket. Reconnect-window duration is unchanged.
- **Regression coverage:** `op_clemnt.test.js` adds same-tick replacement for death / hidden / self-bound /
teleport immunity / failed path, the 15-slot and weight-4 probes, and a changed current weight. The eight regressions
fail before the fix and pass after it. `client-static.test.js` covers live / reload / mid-init, no fallback identity,
welcome-only persistence, earlier-welcome priority and a welcome arriving during init. The real-browser recovery
test holds a refreshing owner's initialization open while another page clicks recovery and checks both server
identities and live sockets; closed-window recovery also checks that welcome arrives without a page reload.
`client-identity-handshake.test.js` controls delayed hellos / welcomes through the real client and server handlers,
including a later holder's buffered reconnect, same-stamp reconnect, stale-welcome action suppression and validation.
- **Golden:** all 285 scenarios regenerated. Only `diy-135` moves: releasing S2 capacity permits more later carries
(teleport events 572 → 630), changing movement, attack/heal timing and RNG consumption. Clear time 194.33 → 198.03 s;
outcome remains `cleared`, 150/150 kills, zero leaks, one allied death and zero errors. The other 284 scenarios and
the `roster`, `bonds`, `fields`, `matches`, `standins` families are unchanged.
- **Contracts updated:** design/engine.md, SIM.md (cabin), design/client.md, design/network.md, META.md and PLAYING.md
(local identity recovery and handshake).