RELEASE
planning/2.8.5/audits/FO3/AUDIT_FO3_U8.md
sha256 07e10a799c5cb0ec · 16014 bytes ·
original held in the private archive
# FO3 PIP-BOY — UNIT U8 · PROTOCOL 8 STAGE 3 (INDEPENDENT AUDIT)
**Auditor:** Opus (stage 3 — independent audit). **No code changed. No commit. No push.**
**Commit audited:** `62ff4ab` on `dev` ("FO3 U8"). **Predecessor checklist:** `planning/AUDIT_FO3_U7.md`.
**Method:** started from the diff and the pixels, not the implementer's report. Read the figure
markup, the limb apply loop, the CSS grid that flanks the figure, the render-integrity allowlist
change, and the full test diff. Took my own Playwright screenshots (my own harness, not the
implementer's saved PNGs) of STATUS with limbs crippled on the LEFT only, the RIGHT only, and ALL
limbs incl. head; SPECIAL/SKILLS/PERKS/GENERAL/MANIFEST; FO3 portrait; FO3 desktop; and NV at 360.
Read the computed colour of every element on the FO3 glass. Ran the 3199-test Node gate myself
(all pass). Verified the MANIFEST filter is still reachable in both orientations.
**Verdict: BACK TO STAGE 2.** Most of the punch-list genuinely landed — but the unit ships a **live
mirrored-controls bug** (the one the owner already reported) and a **false user-facing CHANGELOG
line**. Both are blocking. Details below.
---
## ⭐ THE ONE-LINE ANSWER (for the phone)
**The owner is right, and I found exactly why. On the Fallout 3 character screen, the body-part
boxes are laid out on the *opposite* side from the matching arm/leg on the Vault Boy figure — so
tapping "L.ARM" (which sits on the left) lights up the figure's *right* arm, and vice versa. Nobody's
data is wrong — tapping L.ARM really does record a hurt left arm — but the boxes and the figure point
at opposite sides, so it *looks* mirrored, exactly as reported.** Separately, the release notes claim
"fixed the last remaining red element on the screen," and that's **not true** — there are still
several red things on the Fallout 3 glass (a red ✕ next to your status effects, the radiation
readout, a red "RadAway needed" warning, and a pulsing red edge glow when your health is low). The
new figure itself looks great and reads clearly at real size, the "CRIPPLED" wording and green perk-
delete button both landed, and New Vegas is untouched. But the mirrored taps and the false red claim
have to go back for another pass.
---
## ⭐⭐ THE MIRRORED-LIMB BUG — CONFIRMED, ROOT CAUSE NAILED
**Confirmed with my own eyes and the live DOM, on BOTH FO3 landscape and FO3 desktop.**
I crippled only the two LEFT boxes (L.ARM + L.LEG) and screenshotted STATUS: both "CRIPPLED" marks
appeared on the figure's **right** side. I then crippled only the two RIGHT boxes (R.ARM + R.LEG):
both marks jumped to the figure's **left** side. That is the owner's report, reproduced exactly.
### What is actually happening (precise root cause)
Three things each have to be checked separately — the state wiring, the figure drawing, and the box
placement. Here is what each one does:
1. **The control boxes are wired correctly.** `btn_l_la` calls `toggleLimb('la')`, which writes
`state.la` (the left arm). The box labelled L.ARM really does control the left arm. Same for all
five. (index.html ~1704-1750; the labels and handlers match.)
2. **The figure is drawn correctly (anatomically).** In the SVG, `data-limb="la"` (left arm) is
drawn on the **viewer's right** (x ≈ 194–296 in a viewBox centred at x≈170); `data-limb="ra"`
(right arm) is drawn on the **viewer's left** (x ≈ 56–146). Legs the same: `ll` right,
`rl` left. That is the correct front-facing convention — a person facing you has their left arm on
*your* right — and it is what Fable's mockup documents. The apply loop toggles `.crippled` on the
matching `[data-limb]`, so the figure always dashes the anatomically-correct limb for the state.
The **display is NOT lying** about which real limb is hurt.
3. **The control boxes are placed on the WRONG side.** The CSS grid
(`css/60-fo3-pipboy.css` ~772–806) puts **HEAD / L.ARM / L.LEG in the left column** (grid-column
1) and **R.ARM / R.LEG in the right column** (grid-column 3). So the L.ARM box sits on the
viewer's **left**, but the figure limb it controls (`la`) is drawn on the viewer's **right**. Every
arm/leg box is on the opposite side of the figure from the limb it toggles, and the little dash
"leader lines" (`::after`, ~829-851) reinforce the wrong pairing.
**So which side is wrong?** Neither the wiring nor the figure is individually wrong. The figure uses
the anatomical (front-facing) convention; the boxes are placed by naive screen-side (L on the left).
The two conventions **contradict each other**, and that contradiction is the whole bug. It is a
**layout mismatch — the box column assignments** — not a mislabelled figure and not a mis-wired
handler.
**Is the damage DISPLAY mirrored / misinforming?** No — not in the dangerous sense. If the state says
the right arm is crippled, the figure dashes the limb on the viewer's left, which *is* the character's
right arm (front-facing). The figure never shows the wrong anatomical limb. The only confusion is
spatial: the dashed figure limb is on the opposite side from the box you tapped. So a player who
trusts the box **labels** records their damage correctly; the figure just appears to react on the
"wrong" side. That makes this a serious UX/coherence bug, but **not** a silent data-corruption bug.
**Does the bug live in the boxes too, or only the figure?** The box **labels and wiring are fine** —
no relabelling is needed. Only their **column placement** is on the wrong side relative to the figure.
### The unambiguous fix (for Stage 2 — do NOT apply here)
Keep the anatomically-correct figure and **swap the two box columns**: move L.ARM / L.LEG to
grid-column 3 (the viewer's right, beside the figure's `la`/`ll`), and R.ARM / R.LEG to grid-column 1
(beside `ra`/`rl`), and swap the matching `::after` leader-line side rules. Then each box sits next to
the exact limb it controls and the perceived mirror disappears. (The alternative — redrawing the
figure un-mirrored — is worse: it breaks Fable's approved mockup and the front-facing convention.)
Files: `css/60-fo3-pipboy.css` grid rules ~782–806 and leader-line rules ~829–851.
**Why it escaped the gate:** the new tests confirm the figure has five `data-limb` groups and that
the apply loop toggles `.crippled` — but **no test checks that a box sits on the same side as the
figure limb it controls.** Same shape as the U5 "green gate, invisible panel" escape. The Stage-2 fix
must ship a Protocol 13 regression guard for the box↔figure side correspondence.
---
## THE U7 PUNCH-LIST — VERIFIED EACH, INDEPENDENTLY
### 1 · render-integrity allowlist tightened (bare tag name → dock membership) — ✅ FIXED
The filter no longer matches occluders by bare tag name. `pageProbe()` now records
`hitInBezelDock = hit.closest('.bezel')` on each occlusion (live, inside the page), and
`filterKnownPreexisting()` filters on `!o.hitInBezelDock`. The old
`CONFIRMED_PREEXISTING_DEFECT_BEZEL_DOCK_OCCLUSION_HITS` set (which literally contained `'DIV'`,
`'SPAN'`, `'NAV'`) is deleted. **The hole is genuinely shut, by construction:** an id-less non-dock
`<div>` occluder now reports `hitInBezelDock=false` and surfaces as a failure, where the old code saw
`o.hit === 'DIV'`, matched the set, and forgave it. Tests 224.13a/b lock both the new predicate and
the removal of the old set. Solid.
### 2 · Fable's variant A figure dropped in — ✅ LANDED, and legible at real size
The placeholder stick figure is replaced by Fable's variant A drawing (quiff, face, spread fingers,
condition bars, per-limb CRIPPLED labels). I rendered it at the real 780×360 at 2× **and** raw 1×:
**it holds up** — the face, quiff, individual fingers and dashed limbs all read clearly; Fable's own
worry about the thin stroke turning to mush did not materialise. The **head-wail face swap** on a
crippled head works (verified with all limbs crippled). Legibility: PASS. (The figure being
*anatomically mirrored vs. the boxes* is the separate blocking bug above — the drawing itself is fine.)
### 3 · MANIFEST density — ⚠️ IMPROVED, still marginally under the mockup's 6
The always-visible filter search row now collapses behind a small `⌕` toggle (FO3 landscape only,
hidden by default). My screenshot shows **Test Items 1, 3, 5, 7, 9 fully + 11 partially = ~5½ rows**,
up from U7's ~4–5, against the mockup's 6. Better, honestly disclosed, but not quite 6. Non-blocking.
### 4 · CRIP → CRIPPLED — ✅ FIXED
The limb chips render `[░░░░░░] CRIPPLED` in full. Verified in the live DOM and every STATUS
screenshot. Tests 227.13a/b lock it and guard the old "CRIP" from returning.
### 5 · Perk-delete red ✕ → green — ✅ FIXED
On the PERKS board the SLOT 01/02 delete ✕ buttons are green (`--robco-green`), not red. Verified by
pixels and computed style. Tests 227.14a/b lock it green and guard `--robco-danger` from returning.
---
## ⭐ THE TWO THINGS DISPATCH SAW IN THE SCREENSHOTS — VERIFIED
### A · RED IS STILL ON THE GLASS — ✅ CONFIRMED, and it's MORE than one element
The commit message and the CHANGELOG both call the perk ✕ **"the last remaining red / non-green
element on the glass."** That is **false.** Reading the computed colour of every element on the FO3
landscape STATUS board, these remain red (`rgb(231,76,60)`), all on the landscape screen:
1. **The ACTIVE EFFECTS purge ✕** (`.stlamp-purge`) — the red ✕ next to "Well Rested". Visible in
the viewport. (The implementer disclosed this stays red — but the CHANGELOG line contradicts the
disclosure.)
2. **The RAD (RADS) value field** (`#stat_rads`) — red text at high radiation.
3. **The "RAD TREATMENT / RadAway needed — NONE IN PACK" alert** (`#radAwayAlert` + its lamp) —
bright red, and quite prominent when the vitals column is scrolled.
4. **The critical-HP edge vignette** (`body.hp-critical-vignette .glass-frame`,
`css/55-feedback-animations.css`) — a pulsing red inset glow around the **entire** glass whenever
HP < 25%. It is **unscoped** (shared global, applies on every FO3 board), so it reddens the whole
Pip-Boy edge. Pre-existing, not introduced by U8 — but it is red on the FO3 glass and contradicts
"one green."
So "the last remaining red element" is a **false user-facing CHANGELOG claim** — the exact class of
defect this stage exists to catch, and the exact thing Dispatch asked me to confirm. The perk ✕ fix
is real and good; the *claim wrapped around it* is not.
### B · RIGHT-HAND VITALS COLUMN CLIPPING — ⚠️ PARTIALLY still clipping
The vitals panel is heavily scroll-gated: `#opVitalPanel` measures scrollHeight **495px** inside a
**141px** visible box (28% shown). In the populated state, the **RAD value/slider clips** at the
panel's bottom edge and the RAD-AWAY alert sits below the fold; the CONDITION (HP 22/100 CRITICAL) and
the "Well Rested" effect name + ✕ are visible. So it is **not** the clean "radiation and active
effects visible without scrolling" the U7 changelog claims — the effect name is visible, but the
radiation *value* still clips. This is inherited from U7 (U8 did not touch the vitals layout), so it
is not strictly U8's regression, but the standing claim is optimistic. Non-blocking for U8 on its own,
but note it did not get better.
---
## ALSO VERIFIED
- **Nothing became read-only.** MANIFEST's filter search is still fully reachable: on FO3 **landscape**
the `⌕` toggle reveals the row and focuses the input (probed: closed→open, input reachable); on FO3
**portrait** and NV the row stays always-visible (probed: rowVisible=true, input reachable) — because
both the row-hide and the toggle-reveal rules are correctly inside the `@media(orientation:landscape)`
block. One extra tap on landscape, **no capability lost.** Perk delete stays one-tap; STATUS steppers
present.
- **Test-suite integrity — no covert weakening.** Count moved **3187 → 3199 (+12)** and it reconciles
exactly: 224.13 (×2), 227.6e (×1), the 227.11c swap (net 0), 227.12 (×5), 227.13 (×2), 227.14 (×2).
The one assertion that *looks* narrowed — 227.6a's regex changed from
`<g class="limbline" data-limb=…>` to `<g data-limb=…>` — is a **faithful update to the restructured
markup** and still requires exactly five correctly-keyed limb groups; 227.6b actually got
**stronger** (exactly five `slottxt` CRIPPLED labels vs. mere presence). No regex was quietly
loosened. **I ran the runner myself: all 3199 pass.** (The only real gap is *missing* coverage for
the box↔figure side correspondence — see the mirror section.)
- **NV untouched.** My NV 360 screenshot renders correctly (cyan scheme, legacy top-down zone plate,
flat layout); the diff introduces no NV selectors. PASS.
- **Protocol 38.** Every new FO3 rule (figure classes, limb grid, MANIFEST toggle) is scoped
`[data-game='FO3']`; `toggleManifestFilterRow()` is game-agnostic (flips `hidden`, no game literal).
PASS. (The red critical-HP vignette is a pre-existing unscoped rule, not a U8 addition.)
- **Protocol 1.** Cache bumped `r23 → r24`. PASS.
- **Protocol 2/2a.** Counts synced to 3199 across CLAUDE.md / README.md / ARCHITECTURE.md / RULES.md /
CHANGELOG.md; no stale 3187 anywhere. PASS.
- **Protocol 13.** Each landed fix ships a guard (224.13, 227.12/13/14, 227.6e) — **but the headline
mirror bug has no test and is not fixed.**
- **Protocol 41.** Tree clean (my screenshots live under gitignored `planning/`).
- **Deploy.** `62ff4ab` is on `origin/dev` (correct — unreleased work stays on `dev`, Protocol 43).
---
## CHANGELOG ACCURACY — every new line checked against the running app
- "Redrew the Vault Boy figure… quiff, face, spread fingers… crippled head swaps to a distressed
face" — ✅ **true** (verified).
- "cargo screen now shows another row of items… filter search box tucks behind a small tap-to-reveal
search button… filter still there, one tap away" — ✅ **true** (≈5½ rows, `⌕` toggle, reachable).
- "crippled-limb readout… now spells out CRIPPLED in full" — ✅ **true**.
- **"Fixed the last remaining red element on the Fallout 3 Pip-Boy's landscape screen"** — ❌ **FALSE.**
Multiple red elements remain on that very screen (§A above). This line must be corrected.
---
## VERDICT: BACK TO STAGE 2
Good work landed — the render-integrity hole is genuinely shut, Fable's figure is in and legible, the
CRIPPLED wording and green perk-delete are done, NV is untouched, and the tests are honest. But two
blocking issues stand:
1. **[BLOCKING] The mirrored limb controls.** A live bug in the owner's hands: tapping L.ARM/L.LEG
lights up the figure's right side and vice versa, on both FO3 landscape and desktop. Root cause: the
box grid columns are placed on the opposite side from the anatomically-drawn figure limbs. Fix: swap
the L/R box columns (and their leader-line sides) in `css/60-fo3-pipboy.css`, keeping the figure as-
is. **Must ship a Protocol 13 regression guard** asserting each box sits on the same side as the
figure limb it controls — the current tests do not cover this.
2. **[BLOCKING] The false "last remaining red element" CHANGELOG line** (and the same claim in the
commit message). At minimum the line must be corrected to the truth. Better: also decide what to do
about the remaining reds — the ACTIVE EFFECTS ✕, the RAD value, the RadAway alert, and the global
critical-HP vignette — under the "one green" rule, and make the changelog say only what's true.
3. **[NON-BLOCKING, carry forward] MANIFEST at ~5½ rows** (target 6), and the **right vitals column
still scroll-gates the RAD value** (the U7 "visible without scrolling" claim is optimistic).
---
*End of Stage 3. No code was changed, no commit made, no push. Evidence screenshots saved as
`planning/mockups/fo3/audit-u8-*.png` (gitignored).*
STAMP · generated for RELEASE v2.8.5 commit 06e5180 (06e51801b38a) · archive input-tree hash c07fbfbdd2e1ddeb · 754 files · no wall-clock timestamp (regenerates identically when nothing changed).