diff --git a/context/pod-hardware.md b/context/pod-hardware.md index 4bcb3f5..206c5b7 100644 --- a/context/pod-hardware.md +++ b/context/pod-hardware.md @@ -424,6 +424,65 @@ returns nothing rather than failing. Kill `btl4.exe` BEFORE scp'ing a new exe or locked. The receipts in `podrun.log` are the remote eyes; a missing `DEBUG_STREAM` line in an otherwise-logging run is real evidence that code path did not execute. +### The remote runbook — READ THIS BEFORE IMPROVISING (2026-08-08) +Every line below cost real time to rediscover on a live stream. Follow it in order. + +**1. Connect.** `ssh bt411-pod` (alias in `~/.ssh/config`) → `bt411-pod.tail840fa4.ts.net`, user +`user`, key `~/.ssh/bt411_pod`. +⚠ **The key has existed since 2026-08-06 but ssh will NOT offer it without the config entry** — it +only tries default names (`id_rsa`/`id_ed25519`). Without it you get +`Permission denied (publickey,password,keyboard-interactive)`, which reads exactly like "auth was +never set up". It was. Also: the Tailscale **node** KeyExpiry (2027-02-02) is what keeps the cab on +the tailnet — it is **not** an SSH credential, and Tailscale SSH is **not** enabled on the pod (the +peer advertises no `sshHostKeys`), so port 22 is the pod's own Windows OpenSSH. + +**2. Absolute paths for System32 tools.** Over this SSH+cmd session, bare `taskkill` / `setx` +return `The system cannot find the path specified.` and silently do nothing. Use +`C:\Windows\System32\taskkill.exe`. (`tasklist`, `schtasks`, `dir`, `copy` happen to resolve.) + +**3. Deploy — the SAME procedure a tester uses.** No pod-special exe drops. +``` +python tools/mkdist.py # -> dist/BT411_4.11.NNN.zip +scp dist/BT411_4.11.NNN.zip bt411-pod:C:/bt411/ +ssh bt411-pod "powershell -NoProfile -Command \"Expand-Archive C:\bt411\BT411_4.11.NNN.zip -DestinationPath C:\bt411 -Force\"" +ssh bt411-pod "powershell -NoProfile -ExecutionPolicy Bypass -File C:\bt411\podkit.ps1 -Content C:\bt411\BT411_4.11.NNN\content" +``` +`runpod.bat` auto-resolves the **newest** `BT411_*` by date, so nothing else needs pointing. +Local config is **never clobbered**: `mkdist.py` packs git-TRACKED content only, and +`bindings.txt`/`environ.ini`/`glass_layout.cfg` are all gitignored. `podkit.ps1` pushes the frozen +rig masters (`podprofile.ini`, `glass_layout.cfg`, `PODTEST.EGG`) from `C:\bt411\` into the install. +⚠ **`PODTEST.EGG` is not in the repo** — only podkit carries it. If podkit fails, the launcher runs +and *nothing appears*, with no error. + +**4. Launch.** `schtasks /run /tn BT411Run` → runs `C:\bt411\runpod.bat` as `user`, +LogonType=Interactive, so it lands in **session 1** and is visible on the panels. Log: +`\content\podrun.log`. + +**5. ⚠ THE PODKIT INFINITE LOOP — the trap that ate an evening.** `podkit.ps1` line 30 was +```powershell +while ($keep.Count -gt 0 -and $keep[-1].Trim() -eq '') { $keep = $keep[0..($keep.Count-2)] } +``` +When `$keep` trims to ONE blank line, `$keep.Count-2` is `-1` and PowerShell's `$keep[0..-1]` +returns **two** elements instead of shrinking — infinite loop, RSS climbing past 60 MB. It fires +whenever everything outside `environ.ini`'s marker block is blank, i.e. **any `environ.ini` that was +already kitted** — exactly what you get carrying it forward from the previous install. Patched to +`-gt 1` on the pod 2026-08-08 (`podkit.ps1.bak` is the original); **this fix is NOT in any repo**, +so a restored/replaced podkit brings the bug back. +*Signature:* a **blank cmd console** on the pod, task stuck `Status: Running`, **no `btl4.exe`, no +`podrun.log`**. *The cascade:* each hung run holds `environ.ini`, so every later attempt blocks too — +and over SSH your client times out while the REMOTE powershell keeps running, so "it returned +instantly and did nothing" actually means "it is still hung". Recover with +`taskkill /F /PID ` (absolute path) on the session-1 `cmd`+`powershell` pair, then +`schtasks /end /tn BT411Run` before re-running — a task already Running refuses `/run` with +`2147946720` (`0x800710E0`, "operator refused the request"). + +**6. Panel identity (measured 2026-08-08, over SSH — WMI is session-independent so this works +without a GUI):** `DISPLAY\RAR0005\…UID224795`, `DISPLAY\DEL4025\…UID249395` (DELL 1908FP), +`DISPLAY\RAR0005\…UID200195`. **The two RAR panels share one EDID code and have blank serials**, so +`monitor:id:RAR0005` is ambiguous — they must be bound by the per-connector form +(`monitor:id:UID224795` / `monitor:id:UID200195`). The Dell's code is unique. The cab's shipped +`glass_layout.cfg` still uses the fragile `monitor:DISPLAY4` device-name form. + ## The 1995 player manual — alignment audit (2026-07-18) [T1, primary source] `reference/manual/Tesla40_BT_manual.pdf` (34pp, from Nick). CONFIRMS the reconstruction on every checked control behavior: diff --git a/tools/podkit.ps1 b/tools/podkit.ps1 new file mode 100644 index 0000000..0df8b0f --- /dev/null +++ b/tools/podkit.ps1 @@ -0,0 +1,58 @@ +# Apply the ALPHA-MR pod kit to a BT411 install's content directory. +# +# WHY THIS EXISTS: the frozen rig config lives in content\environ.ini and +# glass_layout.cfg -- both INSIDE the versioned install folder, and neither is +# shipped in the zip (environ.ini is generated on first run). So extracting a +# new build gives you a cab that comes up wrong with no error anywhere. The +# MASTERS live beside this script at a stable path; this pushes them into +# whichever install you point it at. Idempotent -- safe to re-run. +param([Parameter(Mandatory=$true)][string]$Content) + +$here = Split-Path -Parent $MyInvocation.MyCommand.Definition +$prof = Join-Path $here 'podprofile.ini' +$lay = Join-Path $here 'glass_layout.cfg' +$ini = Join-Path $Content 'environ.ini' + +if (-not (Test-Path $Content)) { Write-Output "NO SUCH CONTENT DIR: $Content"; exit 1 } +if (-not (Test-Path $prof)) { Write-Output "MISSING MASTER: $prof"; exit 1 } + +# --- the env block: replace anything between the markers, keep the rest ------ +$block = Get-Content $prof +$keep = @() +if (Test-Path $ini) { + $inBlock = $false + foreach ($line in (Get-Content $ini)) { + if ($line -match '^# ==== BT411 POD PROFILE') { $inBlock = $true; continue } + if ($line -match '^# ==== END BT411 POD PROFILE') { $inBlock = $false; continue } + if (-not $inBlock) { $keep += $line } + } + # ⚠ `-gt 1`, NOT `-gt 0` (fixed 2026-08-08). With `-gt 0`, once $keep trims + # down to a SINGLE blank line, $keep.Count-2 is -1 and PowerShell's + # $keep[0..-1] returns TWO elements (index 0 and index -1 = the last) instead + # of shrinking -- so the array grows and this loops FOREVER, RSS climbing past + # 60 MB. It fires whenever everything outside environ.ini's marker block is + # blank, i.e. any environ.ini that was ALREADY kitted -- exactly what you get + # carrying config forward from the previous install. Symptom on the cab: a + # blank cmd console, BT411Run stuck "Running", no btl4.exe and no podrun.log, + # and each hung run holds environ.ini so every later attempt blocks too. + while ($keep.Count -gt 1 -and $keep[-1].Trim() -eq '') { $keep = $keep[0..($keep.Count-2)] } +} +($keep + @('') + $block) | Set-Content $ini -Encoding ascii + +# --- the layout: the master always wins ------------------------------------- +# Re-tuning the cab means editing the MASTER beside this script, not the copy +# in the install -- a BT_GLASS_LAYOUT=save drag inside an install is overwritten +# on the next apply, on purpose. One place to look when a panel moves. +if (Test-Path $lay) { Copy-Item $lay (Join-Path $Content 'glass_layout.cfg') -Force } + +# --- the pod test mission ---------------------------------------------------- +# PODTEST.EGG is NOT in the repo -- it was made on the cart, so a fresh extract +# has no mission for runpod.bat to launch (found the first time a new build was +# dropped: the launcher ran and nothing came up). Carry it in the kit. +$egg = Join-Path $here 'PODTEST.EGG' +if (Test-Path $egg) { Copy-Item $egg (Join-Path $Content 'PODTEST.EGG') -Force } + +Write-Output "pod kit applied to $Content" +Get-Content $ini | Select-String '^(BT_|L4)' | ForEach-Object { " " + $_.Line } +if (Test-Path $lay) { Get-Content (Join-Path $Content 'glass_layout.cfg') | Select-String '^[A-Z]' | ForEach-Object { " " + $_.Line } } +