KB: gotcha 34 -- change detection against a shared multi-writer scratch (the #155 previousModeMask class): a change-detector must own its own baseline; audit every manager-scratch read that an authentic high-frequency toggler can starve. Ticket #155 comment 1858 carries the fix + live verification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bw71WVsccjwW7uKRg4aWD
This commit is contained in:
Joe DiPrima
2026-08-14 14:56:23 -05:00
co-authored by Claude Fable 5
parent caa50ef0f6
commit 7b641f9b40
+22
View File
@@ -1106,3 +1106,25 @@ THIS NEVER RUNS *** banner naming the live site (missile.cpp now does). (3) A d
FIXED is a claim, not evidence -- a fix without a FIELD receipt ([incoming], [seqloop], etc.)
is T3 at best. (4) The fix's own bench must fail first: the first #83 bench (solo) produced
zero receipts because solo seats never fill -- proving the rig, then the fix, in that order.
## 34. CHANGE DETECTION AGAINST A SHARED MULTI-WRITER SCRATCH: previousModeMask (#155, 2026-08-14)
The engine ModeManager keeps ONE previousModeMask cell, overwritten by EVERY
Add/Remove/ReplaceModeMask call. Any consumer computing `changed = current ^
manager->GetPreviousModeMask()` only sees the delta of the LAST write -- fine
while exactly one writer touches the mode per frame, silently lossy the moment
a second writer appears. The authentic panic-arm chatter (the no-hysteresis
eject evaluator flapping ModeEject per frame) made the gauge renderer's page
XOR permanently blind to SetPresetMode's page bits: MFD page EXITS died while
the PANIC lamp flickered (entries survived via !alreadyActivatedFlag; the
always-active radar/score/coolant never consult the XOR -- the field's exact
alive/dead split). The renderer ALREADY OWNED a correctly-maintained
previousModeMask member; reading the manager's scratch instead looks like
lineage drift.
**Rules:** (1) a change-detector must own its OWN baseline snapshot, sampled
and advanced at ITS consumption rate -- never a shared scratch another writer
can stomp between samples. (2) When an authentic high-frequency toggler
exists (chatter, flap, blink), audit every `GetPreviousModeMask`-shaped read
it can starve (LAMP.cpp's read survives because lamp flicker IS the intended
output). (3) The live-human receipt loop cracked what two headless rigs
could not: the injection path was clean, so the defect HAD to be presentation
-- receipts proving delivery while the screen disagrees convict the renderer.