# gauge-ab -- the cockpit A/B rig Runs the reconstruction and the shipped 4.10 binary against the **same mount** (same `GAUGE/L4GAUGE.CFG`, same egg, same mech) and compares what each one paints into the gauge framebuffer. With `L4VIDEO=OFF` and `L4GAUGE=640x480x16`, the DOSBox window *is* the packed gauge framebuffer -- the exact buffer the pod's VDB splits into its physical heads. So a screen grab is a lossless read of the cockpit. ## Running ```bash bash ab.sh rec # stage the fresh build + run the reconstruction bash ab.sh shp # run the shipped binary powershell -File grab.ps1 -Out ours.png python3 planes.py shipped.png ours.png ``` **Always launch through `ab.sh`.** The confs run `BTL4REC.EXE` out of the mount, *not* `build410/btl4opt.exe`. Forgetting to re-stage silently measures the previous binary -- on 2026-07-27 that cost a full round of wrong conclusions (a fix "did nothing" three times because the tested exe never changed, and the correct first hypothesis was discarded on that evidence). `ab.sh` re-stages every time and kills any running DOSBox first (two instances share `OUT.TXT` and corrupt the trace). ## The instruments ### `planes.py` -- the per-head scoreboard (use this first) The framebuffer is **plane-packed**: `L4GAUGE.CFG` gives each of the ten logical ports a *bit mask*, not a rectangle -- ``` configure(8,Heat, 0,0x4000,clut2,blue, NULL); # UL configure(9,Comm, 0,0x8000,clut2,red, NULL); # UR ``` -- so all six heads occupy the same x/y and are separated only by bit. RGB comparison is therefore meaningless: a "magenta artifact" is really *the wrong heads lit at that pixel*. DOSBox presents the buffer as RGB565 with bit replication, so the original 16-bit word is recovered exactly by `R>>3, G>>2, B>>3`. `planes.py` reports, per head, how many pixels the shipped binary lights, how many we light, how many we're **missing**, how many are **extra**, and the bounding box of the extras. Missing vs extra is the useful signal: extras are *our* bugs, missing is unbuilt work. ### `score.py` -- one whole-frame number Coarse (the big MFD heads dominate it). Good for "did this regress", useless for locating anything. Prefer `planes.py`. ### `BT_VIS_LOG` -- the widget map `set BT_VIS_LOG=1` in the conf makes the build print every gauge widget the interpreter constructs, with its port and authored position: ``` [w] pilotList port=9 at 0,0 [w] rankAndScore port=9 at 33,0 [gen] port=0 x=443 y=322 lampA='sgena.pcc' ... on=1 col0=3 col1=9 [cluster] port=4 x=0 y=240 title='qsensors.pcc' banner=1 ``` The `[w]` line comes from the single dispatch every widget is built through (`MethodDescription::Execute`, MUNGA/GAUGREND.CPP), so the map is complete. This is what turns "a diff at framebuffer (67,3)" into "that head has no drawing widget there, so look elsewhere". ## Method Source inspection has now produced **five** wrong suspects on this one artifact; each was settled -- or killed -- by one instrumented run. So: 1. `planes.py` first: *which head*, and is it missing or extra? 2. `BT_VIS_LOG`: which widgets exist on that head, at what position? 3. Only then read code, and only that widget's. And re-stage before believing any measurement. ## Positive controls: making an invisible bug measurable A fix that makes something *disappear* cannot be verified on a screen where nothing was visible in the first place. The translation-table artifact (BT410 5.3.83) is the worked example, and the trick generalises. `vis_provoke.conf` sets `BT_TRANS_PROVOKE=1`, which makes the `BTL4GraphicsPort` ctor (source410/MUNGA_L4/L4GREND.CPP) fill the colour head's uninitialised `translationTable[64..255]` with `0xFF00` -- every high-byte head bit -- instead of the zero the fix installs. Any draw that indexes the tail then lights *all* the mono heads at once, so: ``` bash ab.sh rec # baseline, tail = 0 ... -conf vis_provoke.conf # same binary, tail = 0xFF00 python3 planes.py shipped.png ours_provoke.png # Eng1/2/3 extras = the hit count ``` On the boot cockpit that is **1030 pixels**. With the fix it is **0**. The bug was always firing; the heap simply happened to hold zeros. `oormask.py shipped.png ours_provoke.png mask.png` then renders *where* those pixels are and prints their horizontal run lengths. Solid runs mean a rectangle in the source art, not scattered noise -- that is what identified BTSEC1.PCX's 52x13 block of index 254, which the port's 270-degree rotation lands at screen (526,228)-(538,279). `barbox.py ` reports what each build draws inside that box in the sec plane, which is how "all five captures are identical here" got established. **The generalisable move:** when a fix replaces garbage with a benign value, build the variant that replaces it with a *maximally loud* value. That converts "I cannot see any difference" into a number, and it distinguishes "the fix works" from "this screen never exercised the path". ## Noise floors are not interchangeable Three grabs of one running instance scored *identically* on the MFD heads, so the within-boot noise floor is zero. That floor is the wrong one: it says nothing about a fresh process. Re-booting the *same* binary moves the MFD heads by ~6px, and only that across-boot floor can judge a rebuild. A first "fixed" capture appeared to regress the MFDs by ~2700px and nearly got a correct change reverted. It was a bad grab caught mid-draw. Two agreeing runs, compared against the across-boot floor, is the rule.