Issue #33/#34: relaunch-storm + duplicate-seat fixes (pod-grade session stability)

Night-2 capture: one flapping pod cascaded 3 ROUND ABORTs in 8s; every
client relaunch-churned (~1 gen/s) until the D3D device-creation 20s
deadline fired on every machine ('could not connect direct 3d'), and
claim-less restarts minted duplicate roster seats.

Console (relay):
- Round abort clears stale console-conn ACK flags and HOLDS the next
  egg release for an 8s settle window (_tick_settle re-releases when
  quiet) -- the abort->instant-re-release->drop->abort cycle is broken.
- HELLO from a stale identity gets a REJOIN reply (identity resync via
  the seat-request path, 5s/IP rate limit) instead of a bare drop.
- STATIC SEATS: seats remember (IP + callsign); a claim-less restart
  from the same machine+name RECLAIMS its own seat (displacing its
  zombie beacon) instead of minting a duplicate -- the pod contract:
  same box, same seat, always.
- Operator local instances log with BT_LOG_APPEND (the night-2 crash
  loop left a 2-line file; generations now leave a full trail).

Client:
- Relaunch storm damper: a generation that lived <15s sleeps 5s before
  respawning (storm decays to a gentle cadence; normal multi-minute
  round relaunches untouched; menu clicks explicitly never delayed).

Rig-verified: normal round + mid-MISSION drop = no abort (correct);
mid-LOAD kill = exactly ONE abort + 8s hold + seat reclaimed + no
cascade.  The night-2 'host 7' identity-derivation subtlety remains
under forensics (append logs + first-breath line will capture it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-23 23:35:25 -05:00
co-authored by Claude Opus 4.8
parent 26439c2e1a
commit d3ede2e635
4 changed files with 163 additions and 4 deletions
+10 -1
View File
@@ -894,7 +894,16 @@ class Operator(QMainWindow):
host, _, port = tag.rpartition(":")
net_port = int(port) - 1
args += ["-net", str(net_port)]
env.insert("BT_LOG", "operator_%d.log" % (r + 1))
# issue #33 forensics: append across the menu->game relaunch
# chain (each generation used to TRUNCATE the log -- the night-2
# crash loop left a 2-line file). Fresh file per launch press.
log_name = "operator_%d.log" % (r + 1)
try:
os.remove(os.path.join(CONTENT, log_name))
except OSError:
pass
env.insert("BT_LOG", log_name)
env.insert("BT_LOG_APPEND", "1")
proc = QProcess(self)
proc.setWorkingDirectory(CONTENT)
proc.setProcessEnvironment(env)