Second operator report, same day: "what is that yellow orange artifact on the
display, it too shows up here and in bt411 but not in the original" -- a
solid vertical bar beside the RANGE readout on the colour head.
It is the same defect as 5.3.83's MFD bleed. Not a similar one: the same
676 pixels.
HOW IT WAS CAUGHT. 5.3.83 shipped a fix that could not be verified, because
the fix makes something INVISIBLE and the A/B rig's boot cockpit already
showed zero MFD extras -- the unfixed build looked perfectly clean. So this
commit adds a POSITIVE CONTROL instead of another argument:
BT_TRANS_PROVOKE fills the colour head's uninitialised
translationTable[64..255] with 0xFF00 -- every high-byte head bit --
rather than the zero the fix installs. Any draw that indexes the tail
then lights ALL the mono heads at once.
On the boot cockpit that lights exactly 1030 pixels: Eng1/Eng2/Eng3 +1030
each, Mfd1 +684, Mfd2 +704, Mfd3 +1074, Comm +676, against 0 extras with the
fix. The bug was firing the whole time. Our heap simply happened to hold
zeros in that tail -- the same luck the shipped binary has been having, which
is exactly why the operator sees the artifact and the A/B rig does not.
THE OFFENDER, NAMED. oormask.py renders which pixels those are and prints
their horizontal run lengths. The mask is a glyph cluster plus 52 runs of
13px -- a SOLID 13x52 BAR at screen (526,228)-(538,279). Solid means a
rectangle in the source art, so decode the art:
BTSEC1.PCX, the colour head's own 480x640 background, contains exactly
ONE out-of-range value in the entire image: index 254, exactly 676
pixels, a solid 52x13 rectangle at (199,526)-(250,538).
The sec port is configured at ROTATION 270, mapping source (x,y) ->
screen (y, 479-x). That puts the rectangle at screen x 526..538,
y 229..280. Measured mask: x 526..538, y 228..279. Same rectangle, to
the pixel.
ONE READ, TWO SYMPTOMS. translationTable[254] is never written (
BuildSecondaryTranslation fills only 1<<numberOfBits = 64 entries), and
DrawPoint ORs the result in unmasked. Whatever the heap left there decides
which symptom the operator sees:
low 6 bits set -> a coloured block on the COLOUR head, beside the RANGE
readout. The yellow-orange bar.
high 8 bits set -> garbage in the MFD / ENG / COMM planes. The bleed.
Both reports, one uninitialised int. It also explains the "not in the
original" asymmetry without needing the original to differ in code: it does
not differ, it is just getting zeros there. And it explains BT411 showing it
too -- both reconstructions inherit the read from the archive.
5.3.83's zero-fill therefore cures both, and turns a heap-lottery into a
guarantee. Still not DIRECTLY observed cured, because no rig we have was
showing the artifact to begin with; that honesty is recorded in the file
header rather than smoothed over.
ALSO IN: oormask.py, barbox.py, vis_provoke.conf, and a README section on
positive controls -- when a fix replaces garbage with a benign value, build
the variant that replaces it with a maximally LOUD value, because that turns
"I see no difference" into a number and separates "the fix works" from "this
screen never exercised the path".
METHOD NOTE, recorded in the README because it nearly cost the fix: three
grabs of one running instance score IDENTICALLY, so the within-boot noise
floor is zero -- and that is the wrong floor. Judging a rebuild needs the
ACROSS-boot floor (~6px on the MFD heads). A single bad grab, caught
mid-draw, read as a 2700px regression and nearly got a correct change
reverted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 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:
planes.pyfirst: which head, and is it missing or extra?BT_VIS_LOG: which widgets exist on that head, at what position?- 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 <pngs...> 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.