The host clears seats and closes rooms

Two lobby-lifecycle gaps from tonight's duels (a token lost to a
client bug left an unkickable ghost seat holding a chair in VQJW).
kickSeat: the host removes any other seat from an unstarted room —
the kick is a ledger line so restarts replay it, and a kicked live
socket is set adrift with notice. abandonRoom: the host dissolves a
lobby or a finished game; every member is notified and detached, and
the ledger is archived to rooms-abandoned/ — moved, never deleted.
The lobby shows hosts a ✕ per seat and an abandon-room control.
Proven live: kick with notice, kick surviving a server restart, and
abandonment archiving its ledger.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
Eric Wagoner
2026-08-26 11:05:42 -04:00
co-authored by Claude Fable 5
parent 4c7633097b
commit 9af7bd80ce
5 changed files with 130 additions and 4 deletions
+8
View File
@@ -1812,6 +1812,10 @@
<span class="dot" style:background="#b3a687"></span>
{/if}
{p}{p === net.hostId ? " — host" : ""}{net.roomBots[p] ? ` ⚙ ${net.roomBots[p]}` : chosen === undefined ? " — choosing…" : ""}
{#if net.you === net.hostId && p !== net.hostId}
<button class="stamp tiny kick" title="clear this seat"
onclick={() => net.kickSeat(p)}>✕</button>
{/if}
</li>
{/each}
</ul>
@@ -1835,6 +1839,8 @@
{/each}
</div>
{#if net.you === net.hostId}
<button class="stamp tiny abandon" title="dissolve this room for everyone"
onclick={() => net.abandonRoom()}>abandon room</button>
{#if net.players.length < 6}
<span class="bot-row">
<span class="bot-label">⚙ seat a</span>
@@ -2462,6 +2468,8 @@
text-transform: uppercase;
color: #8d8672;
}
.kick { margin-left: 0.4rem; color: #a04545; }
.abandon { margin: 0.4rem 0; color: #a04545; border-color: #a04545; }
.mast-version {
font-family: "Courier Prime", monospace;
font-size: 0.65rem;