diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md new file mode 100644 index 0000000..0e374e2 --- /dev/null +++ b/docs/HANDOFF.md @@ -0,0 +1,113 @@ +# Road Handoff — where the work stands + +*Snapshot: 2026-07-27 morning, at commit `39cb864`. This is a living file; update it or delete it +when the picture changes. Everything below was checked against the machine/tracker, not memory.* + +--- + +## 0. Operational state RIGHT NOW (read this first if you want to host) + +| Thing | State | +|---|---| +| **Parked relay** | **NOT RUNNING.** Ports 1500 / 1501 / 1507 have no listener. | +| Orphan pod | one `btl4.exe` (`OPERATOR.EGG -net 1501`) left over from last night, connected to nothing. Safe to close. | +| RDP into the home machine | enabled, 3389 listening, rules in place | +| Mesh VPN (Tailscale) | **not installed** — the control port is not tunnelled | +| Release `4.11.600` | cut, verified, **not yet posted** | + +**To host from the road you must first get the relay up on the home machine** — a remote console +*attaches*, it cannot start one. Either RDP in and double-click `tools\park_relay.cmd`, or put it in +`shell:startup` so it survives a reboot. Full runbook (what a remote console can and cannot do, the +control-port table, the secret's working-directory trap): **`context/operator-console.md` §Parked +relay + remote console**. + +⚠️ Forwarding **1507** to the open internet puts the operator secret on the wire in cleartext. +Leave the player ports (1500/1501) as they are and carry the control link over a tunnel. + +--- + +## 1. The release + +`dist\BT411_4.11.600.zip` (47.0 MB) + `dist\RELEASE_NOTES_4.11.600_discord.md` (1,915 chars). + +Verified on the final artifact: doc audit 35/35, bats byte-identical to `players/`, exe identical to +the build at `39cb864`, clean-extract boot with **0 faults**, `bindings.txt` generated with the +`# bindings-board 2` marker, and log rotation proven *inside the shipped zip* (solo run rotates +`solo.log`, leaves a seeded `join.log` untouched). + +Notes cover **584 → 600**, since 584 is the build everyone actually installed. 599 was cut but never +posted; its fixes are folded in. Nothing left to do but post it. + +--- + +## 2. Live investigations (the real work) + +**24 issues are genuinely open; 26 more carry `awaiting-verification`** — fix landed, no human has +seen it work live. Snapshot script: `scratchpad/tracker_snapshot.py` (hits the Gitea API with your +stored git credential; re-run it rather than trusting this list). + +### Blocked on someone else +- **#35 — Owens laser crash.** Waiting on Conn Man's `join.log`. His last one was overwritten, + which is exactly what 600's log rotation fixes, so the next occurrence should survive. + Repro rig is committed: `scratchpad/rig_killstorm.ps1` + the `BT_MP_FORCE_DMG=` hook in + `game/reconstructed/mech4.cpp`. Result so far is an **honest negative** — the kill-storm does not + reproduce it; notes in `scratchpad/crash_20260726_2316_notes.md`. Best current lead: firing at a + *mech* rather than a truck. + +### Ready to pick up +- **#52 — replicant legs "ski"** (slide without walking). The natural sibling to the #67 torso work + just finished: same replication path, same dead-reckoning clock. Start from `torso.cpp`'s + `GetLastUpdateTime()` fix and look for the equivalent on the leg/gait side. +- **#49 — radar/map does not draw the arena WALL geometry.** Audited: there is no wall path at all, + so this is a build-it, not a fix-it. +- **#43, #50, #56** — MFD/roster behaviour reported live but never reproduced on the bench. + +### Deferred with a stated reason (don't re-derive) +- **#55 remaining steps** — the `mechsub.cpp` rename pair, deleting + `HeatableSubsystem::ResetToInitialState`, deleting `RespawnRepair`. All three need a + `HeatableSubsystem` **vtable slot-10 pre-flight**, and there is no binary image in the repo to + dump it from. Steps 0/1/2/4 landed in `ef8e449`. + +### Two calls that are yours, not mine +- **#54** (David's three energy weapons dead) — probably superseded by #57; close it or say why not. +- **#66** (joyconfig.bat hangs after the wizard) — may be invalid; people report using it fine. + +--- + +## 3. What landed in the last 24h and is unverified + +`#67` (both halves), `#55`, `#62`, close-means-quit, four lifecycle-window null-deref guards, the +phantom-map filter, seat identity, console + launcher log rotation, console re-arm. + +**The gate is a live games night.** House rule: nothing moves off `awaiting-verification` until a +human confirms it in a real session — a passing bench test is not verification. + +--- + +## 4. Recent findings worth remembering + +- **The allocator was the bug.** `Memory::Allocate` used plain `operator new`; the pod's + `MemoryBlock` arenas carve OS-zeroed pages. Every 1995 field the constructor never stored read as + zero on the pod and as garbage here. That is #67's root cause, and the same class will bite again + — suspect it whenever a value is wrong only for *remote* entities. (`mechrecon.hpp`) +- **`artrucks` is an art sub-node, not a mission.** Offering it in the operator's map list hung + every pod at "mission starting" for a whole evening. Map anatomy: + `arena1 → {arenall → cavern, artrucks}`. +- **The operator secret follows the relay's cwd.** Two files with different values existed in this + repo; a mismatch shows up as a dropped control connection on timeout, not a "wrong password". + Always park from `content\`. + +--- + +## 5. Picking up on the laptop + +1. `git pull` — everything above is committed, including `scratchpad/` (the repro rigs and crash + notes travel with the repo). +2. **The launcher bats only work from an extracted zip** — they `cd /d %~dp0` and need `build\` and + `content\` beside them. Running them from the repo silently hits `badpath`. This has burned two + verification attempts; extract `dist\BT411_4.11.600.zip` somewhere and test there. +3. Uncommitted and intentionally left dirty: `content/LAST.EGG`, `content/OPERATOR.EGG` (session + state), `scratchpad/sim3.py`. +4. Orientation: `CLAUDE.md` (router) → `context/*.md`. For last night specifically: + `scratchpad/crash_20260726_2316_notes.md` is the full incident ledger, and + `context/steam-networking.md` has the first real multi-player field verification. diff --git a/scratchpad/tracker_snapshot.py b/scratchpad/tracker_snapshot.py new file mode 100644 index 0000000..eea7653 --- /dev/null +++ b/scratchpad/tracker_snapshot.py @@ -0,0 +1,48 @@ +"""Snapshot the OPEN tracker state for the road handoff doc.""" +import base64 +import json +import subprocess +import urllib.request + +REPO = r"C:\git\bt411" +API = "https://gitea.mysticmachines.com/api/v1/repos/VWE/BT411/issues" + +out = subprocess.run(["git", "credential", "fill"], + input="protocol=https\nhost=gitea.mysticmachines.com\n\n", + capture_output=True, text=True, cwd=REPO) +cred = dict(l.split("=", 1) for l in out.stdout.strip().splitlines() if "=" in l) +AUTH = "Basic " + base64.b64encode( + (cred["username"] + ":" + cred["password"]).encode()).decode() + + +def api(path): + req = urllib.request.Request(API + path) + req.add_header("Authorization", AUTH) + return json.load(urllib.request.urlopen(req)) + + +rows = [] +page = 1 +while True: + batch = api("?state=open&limit=50&page=%d" % page) + if not batch: + break + rows.extend(batch) + page += 1 + +awaiting, live = [], [] +for i in rows: + labels = [l["name"] for l in i.get("labels", [])] + rec = (i["number"], i["title"][:78], ",".join(labels)) + (awaiting if "awaiting-verification" in labels else live).append(rec) + +print("OPEN TOTAL: %d awaiting-verification: %d genuinely open: %d" + % (len(rows), len(awaiting), len(live))) +print() +print("=== GENUINELY OPEN (real work) ===") +for n, t, l in sorted(live): + print(" #%-3d %-78s [%s]" % (n, t, l)) +print() +print("=== AWAITING LIVE VERIFICATION (fix landed) ===") +for n, t, l in sorted(awaiting): + print(" #%-3d %s" % (n, t))