parked relay: a supervisor that keeps it alive + a REMOTE restart the operator can actually reach

Completes the travelling-operator deployment.  The relay stays parked on the
home machine (so no player ever edits join.bat) and the console dials in from
anywhere -- but until now two things needed hands on that machine: bringing the
relay back when it died, and restarting it (the only way to clear a wedge or
pick up a roster resize).  Both are covered now.

tools/btrelay_park.py -- the supervisor:
  * runs the relay with cwd=content\ , which is what pins WHICH
    operator_secret.txt is live (the trap documented last commit);
  * relaunches on ANY exit, with 2/5/15/30/60s backoff when a relay dies inside
    20s, so a permanent fault (port taken, missing egg) cannot become a spin;
  * rotates content\parked_relay.log to .1 first, so the dead generation's
    evidence survives the relaunch that replaces it;
  * Ctrl-C stops it for good.  NOT a Windows service on purpose: session 0
    would hide the window an operator wants to tail.
tools/park_relay.cmd -- double-click to park; a shortcut to it in shell:startup
  gives start-after-reboot with no admin rights.

`restart` on the control port (btconsole.py): sets restart_requested, the run
loop returns, relay_main exits RESTART_EXIT_CODE=42 and the supervisor relaunches
-- re-reading the egg.  REFUSED while a mission is running (launches_sent >= 2
and not stop_sent): bouncing then would drop every pod out of a live round.
Local stdin gets the same command for parity.

btoperator.py: Restart Session in REMOTE mode now sends `restart` and reconnects
6s later instead of just tearing down our own link -- the old behaviour looked
like "Restart does nothing" against a parked relay.  (QTimer had to be imported;
it was missing, which py_compile does not catch -- it would have been a runtime
NameError on the first click.)

VERIFIED by scratchpad/test_parked_supervisor.py, 12/12, and the full
test_remote_console_e2e.py re-run green afterwards (15/15):
  * kill the relay -> a NEW pid is serving again, and the old log is kept as .1;
  * remote `restart` -> ACKed, new pid, egg re-read, serving again;
  * the supervisor distinguishes the two -- "operator requested a restart" vs
    the crash/backoff path -- proven from its own log, not assumed;
  * the mid-mission guard is present and wired to launches_sent/stop_sent.

Two harness defects fixed while proving it, both mine: a check written with
`or True` that could never fail (replaced with two real assertions against the
supervisor's log), and a recv that treated the relay's correct
close-after-restart as a ConnectionResetError failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-26 18:48:36 -05:00
co-authored by Claude Opus 5
parent 05f1ffb194
commit 05fdd319d6
7 changed files with 446 additions and 17 deletions
+16 -8
View File
@@ -136,12 +136,13 @@ The recommended setup for a travelling operator: the relay lives permanently on
so **no player ever edits `join.bat`**, and you drive it from a laptop — cell internet is fine,
since the console only makes an outbound connection.
**On the home machine, before you leave:**
**On the home machine, before you leave** — double-click **`tools\park_relay.cmd`** (or run
`python tools\btrelay_park.py --egg OPERATOR.EGG --port 1500`).
```
cd C:\git\bt411\content
python ..\tools\btconsole.py --relay 1500 OPERATOR.EGG
```
That window is the supervisor: it keeps the relay alive, **relaunching it if it ever dies** and
rotating the old log to `content\parked_relay.log.1` so you can still see what happened. Closing
the window (or Ctrl-C) stops the relay for good. To have it come back after a reboot, put a
*shortcut* to `park_relay.cmd` in your Startup folder (Win+R → `shell:startup`) — no admin needed.
Leave it running. Note the port it prints for operator control (**1507** = console port + 7) and
copy `content\operator_secret.txt` — that string is what any console needs to connect, and it grants
@@ -152,9 +153,16 @@ full mission control, so hand it out deliberately.
weather / scenario / temperature / length, applied to the egg on the home machine, effective next
round) and live pilot lights.
**What still needs someone at the home machine:** starting the relay if it is not running,
restarting it, changing the **seat count**, and editing callsigns / mechs / colours — only the six
mission settings travel over the control link.
**Restart Session now really restarts the parked relay.** In remote mode the button asks the relay
to bounce itself; the supervisor brings it straight back with the egg re-read, and your console
reconnects a few seconds later. It is refused while a mission is running (it would drop everyone
mid-round) — press End Mission first. Use it to clear a wedge, or after someone has edited the egg
on the home machine.
**What still needs someone at the home machine:** starting the supervisor in the first place, and
editing the roster itself — seat count, callsigns, mechs, colours. Only the six mission settings
travel over the control link, so a *roster* change means editing the egg there and then pressing
Restart Session from wherever you are.
**Two traps worth knowing:**
- **The secret depends on the relay's working directory.** It is read as a bare `operator_secret.txt`