Commit Graph
423 Commits
Author SHA1 Message Date
James Dansereau 14eb1f8330 findings.md: shrink the 3 multi-label rows with per-sublabel sections to pointers
D76·D164, D165·D166, D180·D181 each point at existing ## Dxx sections that
fully cover the cell content (verified line-by-line; cells were compressed
summaries of the sections). Status cells kept verbatim. The remaining two
multi-label rows (D86·D87, D119·D120) are thin §H-era summary rows with no
sections — deferred to the D70–D121 §H cross-check.
2026-09-15 21:40:23 -04:00
James Dansereau e2b8ec3432 findings.md: consolidate the (a)/(b) duplicate-label sections (D152+, D233, D234)
Each label had a full writeup + a short 'Duplicate row' triage pass.
Keep the full writeup; append the short pass's genuinely-unique content
verbatim under a dated merge note (D233/D234: GRAPHICS-BACKLOG.md logging +
triage hypotheses; D152+: fully subsumed — noted as such). Both §F table
rows stay (cell 1 frozen) and both now point at the single merged section.
Nothing deleted: earlier text in git history + notes/snapshot-pre-ctx-opt/.
2026-09-15 21:39:38 -04:00
James Dansereau 8687846bee findings.md: header documents the ctx-opt migration + CSV lookup path + 4 known index gaps
Cell 1 of the §F table is frozen, so four rows (D69·D78–D84, RC3·D167,
the D88.x range row, D118d) are invisible to findings-index.csv — LABEL_RE
can't parse their label format. Documented in the file header alongside
the lookup convention and a record of what the 2026-09-15 migration did
(including the one non-verbatim step: D250/D252/D255 derived statuses).
2026-09-15 21:39:33 -04:00
James Dansereau 9c9d4a0997 docs(findings): migrate D250/D252/D255 to tail sections (ctx-opt P1, manual)
These three rows were fat topic with NO status cell (2-column rows). Topic
moved verbatim to new ## Dxx sections; table rows get a substantive one-liner
+ a status DERIVED from each entry's own verdict text (D250 FIXED / D252 OPEN
known-issue / D255 OPEN pending rule-#2 sign-off) — the derivation is flagged
in-section for auditability. CSV regenerated, label set unchanged (171); all
three topics verbatim-checked against the snapshot.
2026-09-15 21:30:27 -04:00
James Dansereau 9a8f1c9162 docs(findings): migrate D211/D219 to tail sections (ctx-opt P1, structurally-odd follow-up)
Pure moves following the 6208276d formatting repair: topic/status cells moved
verbatim to new ## Dxx sections; table rows now carry one-line summary +
pointer. CSV regenerated, label set unchanged (171); both sections
verbatim-checked against the snapshot.
2026-09-15 21:29:31 -04:00
James Dansereau 6208276d2b docs(findings): repair pre-existing table formatting in D211/D219/D255 (NOT a content edit)
Formatting fixes only — text preserved verbatim, no content added/removed:
- D211: the HUD ammo display '7 | 93' contained a literal unescaped pipe,
  splitting its status cell in two; wrapped that token in backticks.
- D219: a call-chain used pipes as separators between separate code spans
  (5 cells instead of 3); merged into one continuous code span — same text,
  backtick positions moved.
- D255: row lacked the trailing pipe its neighbours have; added it so the
  table is well-formed 3-column (status cell empty, as in D250/D252).

These were pre-existing CSV corruption (garbled/truncated status_raw), not
migration artifacts. CSV regenerated; label set unchanged (171).
2026-09-15 21:29:20 -04:00
James Dansereau dd2e95e229 docs(findings): repair D233/D234 disambiguation markers (were inserted before '##')
The previous commit's (a)/(b) insertion landed at the line start, producing
' (a)## D233 — …' lines that no longer parse as headings. Moved the markers to
after the label: '## D233 (a) — …'. 117/117 migrated sections now have valid
headings; CSV check + label set still green.
2026-09-15 21:24:21 -04:00
James Dansereau 1726221030 docs(findings): disambiguate duplicate D233/D234 section headings (a)/(b)
Two table rows each carry the same label+topic with a status progression
(earlier vs updated pass). Both migrated verbatim in batch 6; heading markers
are formatting only. Flagged for Bucket B consolidation.
2026-09-15 21:23:47 -04:00
James Dansereau 60b1e4ff61 docs(findings): migrate fat §F rows D271-D294 to tail sections (ctx-opt P1, batch 8/8)
12 rows: topic/status cells moved verbatim to new '## Dxx' sections at file
tail; table rows now carry a one-line summary + pointer. CSV regenerated, label
set unchanged (171). Nothing deleted.
2026-09-15 21:23:27 -04:00
James Dansereau 63b4d79ce6 docs(findings): migrate fat §F rows D244-D270 to tail sections (ctx-opt P1, batch 7/8)
20 rows: topic/status cells moved verbatim to new '## Dxx' sections at file
tail; table rows now carry a one-line summary + pointer. CSV regenerated, label
set unchanged (171). Nothing deleted.
2026-09-15 21:23:27 -04:00
James Dansereau ce4ec5a387 docs(findings): migrate fat §F rows D218-D243 to tail sections (ctx-opt P1, batch 6/8)
21 rows: topic/status cells moved verbatim to new '## Dxx' sections at file
tail; table rows now carry a one-line summary + pointer. CSV regenerated, label
set unchanged (171). Nothing deleted.
2026-09-15 21:23:26 -04:00
James Dansereau 5446cc295b docs(findings): migrate fat §F rows D201-D216 to tail sections (ctx-opt P1, batch 5/8)
10 rows: topic/status cells moved verbatim to new '## Dxx' sections at file
tail; table rows now carry a one-line summary + pointer. CSV regenerated, label
set unchanged (171). Nothing deleted.
2026-09-15 21:23:20 -04:00
James Dansereau 4ea303e25e docs(findings): migrate fat §F rows D184-D199 to tail sections (ctx-opt P1, batch 4/8)
Also disambiguates the two '## D152+' headings from batch 2 as (a)/(b)
(formatting only; content untouched — flagged for Bucket B consolidation).

14 rows: topic/status cells moved verbatim to new '## Dxx' sections at file
tail; table rows now carry a one-line summary + pointer. CSV regenerated, label
set unchanged (171). Nothing deleted.
2026-09-15 21:23:19 -04:00
James Dansereau 2d50dd2319 docs(findings): migrate fat §F rows D169-D181 to tail sections (ctx-opt P1, batch 3/8)
9 rows: topic/status cells moved verbatim to new '## Dxx' sections at file
tail; table rows now carry a one-line summary + pointer. CSV regenerated, label
set unchanged (171). Nothing deleted.
2026-09-15 21:22:48 -04:00
James Dansereau 415abb4438 docs(findings): migrate fat §F rows D152-D168 to tail sections (ctx-opt P1, batch 2/8)
15 rows: topic/status cells moved verbatim to new '## Dxx' sections at file
tail; table rows now carry a one-line summary + pointer. CSV regenerated, label
set unchanged (171). Nothing deleted.
2026-09-15 21:22:47 -04:00
James Dansereau cd6f0b5e83 docs(findings): migrate fat §F rows D123-D151 to tail sections (ctx-opt P1, batch 1/8)
16 rows: topic/status cells moved verbatim to new '## Dxx' sections under a
provenance divider at file tail; table rows now carry a one-line summary +
pointer. CSV regenerated, label set unchanged (171). Nothing deleted.
2026-09-15 21:22:39 -04:00
James Dansereau d7d84c09f5 docs: HANDOFF archive receives M-140 -> ~M-12 block (ctx-opt P0)
Companion to the local split of docs/HANDOFF.md (gitignored rolling
working file — that half of the move is working-tree-only, backed up in
the pre-edit snapshot). Appends lines 137-5764 of the pre-split HANDOFF
(### ARCHIVE — M-140 onward, ~444 KB) after the existing M-2/M-3 brief,
with a divider documenting coverage and the newest-first ordering of
the moved block. Content verbatim; CRLF->LF normalised to match this
file and .gitattributes (eol=lf). Verified: live head + moved chunk
rejoin byte-identical to the original (CR-stripped); appended region
cmp-clean against the snapshot.

Archive intro corrected: it claimed 'through session M-11' but held only
M-2/M-3; the M-4-M-11 gap is pre-existing, noted as such.
2026-09-15 21:11:53 -04:00
James Dansereau eb184b6df1 docs: point auto-loaded context at findings-index.csv, not the §F fat table (ctx-opt P1.5)
AGENTS.md findings bullet + HANDOFF-ARCHIVE.md intro pointer now steer
cold-starting agents to docs/dev/findings-index.csv (grep this, then read
the specific ## Dxx entry) instead of linear-reading the ~100k-token §F
table. Base: release/v0.3.0 @ bf24bef8.
2026-09-15 21:11:00 -04:00
James Dansereau bf24bef8a9 docs(D294): correct trigger condition - ground level, not elevated vantage
User clarified the black-geometry drop-out happened at normal ground
level during normal progression near the statue, not from the
elevated ramp with wide sightlines as the first write-up assumed.
Retracted the "widened FOV frustum from elevation" framing of the
room-visibility hypothesis; kept the underlying room-transition/
streaming-timing guess since it doesn't depend on elevation.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 20:38:43 -04:00
James Dansereau a001f1c137 docs(D294): new finding - Statue ground briefly renders black near the elevated CCCP-monument ramp, self-recovers
User report at Video.FovScale~130 + max draw distance/LOD: ground
geometry near an elevated wide-visibility vantage point drops out to
solid black, then recovers 10-30s later on its own. Doesn't cleanly
match D222 (NPC/object culling, FIXED), D249 (default-FOV far
geometry, doesn't self-recover), or D218 (fog/tint boundary, not
missing geometry) -- logged as its own finding with those three as
neighbors, not a confirmed duplicate of any. Leading unconfirmed
guess: a portal/room-visibility BFS not accounting for the widened
FOV frustum.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 20:37:28 -04:00
James Dansereau b6ed6b1d36 docs: fold D289 into D255 (likely same terminal crash), consolidate D173/D292 as one Bond-positioning thread
User corrections on the playtest batch: D289 (Dam computer-room
crash) is most likely the same computer-terminal SIGSEGV D255 already
fixed this session, not a separate mystery -- needs a re-test on the
post-fix build before treating as new. D292 (Bond sunk into floor on
death) is confirmed the same "Bond's world model positioned wrong"
family as D173/D148, not a separate bug -- cross-referenced both
directions so the split-across-two-files writeup (GRAPHICS-BACKLOG.md
D148 + findings.md D173) is easier to find as one thread covering
level-start floating, cutscene spin, and now death-pose sinking.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 20:35:52 -04:00
James Dansereau 4c2643c239 docs: log Steam Deck playtest feedback batch (D289-D293 new, cross-refs to D173/D219/D243/D246)
User completed a Dam-through-Runway Steam Deck playtest and reported
9 items. Four corroborate already-open findings (D173 third-person
Bond floating at level start, D246 fullscreen edge strips -- Deck's
own default preset sets MSAA=4, exactly this row's leading suspect,
D219 purple/blue particle colors widened to blood decals, D243 Dam
end-cutscene camera glitch -- second independent confirmation). Five
are new: D289 (Dam computer-room crash, non-reproducing), D290 (watch
controller-page missing pad graphic), D291 (Dam truck texture
glitches), D292 (death-pose Bond sunk into floor, possibly D173-
adjacent), D293 (no way to quit the game from the UI -- confirmed in
code, SDL_QUIT via window-close/Alt+F4 is the only path; a QoL gap on
Deck/controller-only setups).

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 20:33:07 -04:00
James Dansereau d475884456 docs(D288): promote D171 (Silo stray triangle) with user-reproduced, visually-documented repro
User reported a stray triangle during the Silo missile-shaft camera
pan (previously D171, parked since M-34 as "observed, not
investigated"). Reproduced headlessly, precisely bounded to frames
~562-741 of a bare -level_20 boot: a large, flat-shaded, untextured
brown triangle (uniform RGB(83,62,44)) grows then shrinks across the
pan, consistent with a real world-space triangle mis-transformed near
the camera plane rather than a HUD element or garbage vertex-color
read. Points at the D106/D233 near-plane/w-clip neighborhood in
port/fast3d/gfx_pc.cpp, not yet confirmed at the code level. Evidence
saved to docs/img/bugs/. Also noted: foreground `timeout N ...` launches
of the exe from this session's Bash tool produce zero GE_PCDUMP output;
backgrounding the launch works reliably -- worth remembering next time.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 20:27:54 -04:00
James Dansereau a2b028737b docs: fix Ourumov spelling in D263 quoted report title
Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 20:19:28 -04:00
James Dansereau 791bf6b7c0 docs(D263): confirm Ourumov/Trevelyan standoff timeout fallback is by design, not a bug
User reproduced the "stuck standoff" 3-4 times; traced to a hard
timeout in ai_22 (label 0x13, UsetuparkZ.c) that fires the execution
unconditionally after ~2.5 min if no other trigger fires first. User
waited it out and confirmed it resolves normally with dialogue -- not
a freeze, not a new bug. Documented so it isn't re-flagged.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 20:17:17 -04:00
James Dansereau 86fdcc1db6 docs(D287): new finding - real non-recovering freeze after long idle + refocus; D252 confirmed on bullet-impact sparks too
D287: user hit a live freeze under debug.ps1 after ~15-20 min idle,
refocus-triggered, non-self-recovering (distinct from D286's single
fire-then-recover shape). Evidence preserved at
scratch/d287_freeze_gdb.txt (untracked, matches this repo's existing
scratch/ convention). Leading lead: gfxFrameMsgQ (32-slot scheduler
client queue) full while the VI-retrace pacemaker keeps ticking,
pointing at the main/render thread's consumer stalling rather than
the scheduler. Not root-caused; GE_D60 diagnostic already exists and
is the recommended next probe.

D252: user confirmed live the rainbow-hue bug still reproduces on
plain bullet-impact sparks, not just explosions -- a much cheaper
repro for the next GE_D252 capture.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 20:00:43 -04:00
James Dansereau 07b25f4b45 fix(D285): lock sndHandleEvent's preempt-scan against sndSetupSound's writer
sndHandleEvent's pool-exhaustion preempt-scan read/walked the shared
D_800243E4 active-ALSoundState list with no osSetIntMask lock, while
sndSetupSound (the list's insert path, reached from sndPlaySfx calls
across 15 game files) already locks it -- an asymmetric-locking gap,
the same D4/D147/D152 "N64 interrupts-off isn't a real lock on PC"
class. Wraps the scan in the identical lock/unlock pair sndSetupSound
already uses, unconditionally (no #ifdef PORT, matching that function's
own style -- osSetIntMask is genuine N64 API and the port's shim is a
process-wide recursive lock, safe to nest). Audited the rest of the
function: the scan is the only D_800243E4 touch point in it.

Build green; -level_09/-level_20 unregressed (1400+ frames, no crash,
no D152 lock-stuck warnings). Not yet gdb/live-confirmed against a
real reproduced crash -- a GE_INPUTSCRIPT-driven stress attempt this
session didn't visibly activate the scripted-input harness for a
reason not root-caused; the honest bar met here is "correct by D4/D152
precedent, build/sweep-clean," not "reproduced-then-fixed."

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 19:35:22 -04:00
James Dansereau 556f4434f3 D243: port GE_D243 front-end MENU_* trace onto release/v0.3.0, build-verify, fix uncached getenv
Cherry-picks the GE_D243 diagnostic (764a1f26, previously stuck on an
unmerged, un-build-verified branch) onto release/v0.3.0. The prior
session's "toolchain blocker" was this session's own TMP/TEMP-vs-Bash-tool
gotcha (documented in porting-notes.md this session), not a real
CMake/ninja issue -- now explained and worked around. Also fixes an
uncached getenv("GE_D243") per menu_init() call (D250's own rule: this
runs ~once/frame during front-end states) to a single cached check.
Build-verified; smoke-tested via a bare -level_33 boot (no crash, no
D243 lines, as expected since bare -level_XX skips the front end).
Still needs a real interactive playthrough to actually correlate
against the D146 stale-DL burst -- documented as the next step in
findings.md D243 (M-141), including an unconfirmed D173-connection
hypothesis raised by the user.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 19:30:58 -04:00
James Dansereau bb1e658ea5 fix(D282): front-end menu nav always uses left stick, matching F10-overlay convention
Menus read the same stick_x/stick_y channel as in-level gameplay, and
naturalPitchMode (SOLITARE-style, default-on) routes that channel to
the right stick for continuous look. Since front.c's menu nav shares
the channel, it silently inherited the right-stick routing, which is
unintuitive on a modern pad/Deck outside a running stage. The gamepad
poll already computed an unused padMenuMode flag for shoulder-button
gating; reused it to always source stick_x/stick_y from the left
stick outside RUN_STAGE, regardless of naturalPitchMode. Port-layer
only, inert during gameplay -- verified via -level_09/-level_20
unregressed; in-menu feel needs a human controller playtest.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 19:28:03 -04:00
James Dansereau 4b1d9bc21c docs(D252): refute width/height suspect via authored asset data; add GE_D252 diag probe
The prior "prime suspect" (import_texture_rgba16 width/height derivation)
assumed line_size_bytes comes from the tile's MASK_16 wrap period, but
assets/oddtextures.c's actual gsDPSetTile for the fire render tile sets
line=4 explicitly, giving the correct 16x14 dimensions -- matching the
pre-existing is_fire_shape==16x14 filter already hardcoded into the
GE_TEXDUMP dumper. Neither named D252 hypothesis (width/height, GEVR's
byte-group-order alternative) survives; root cause stays open. Adds an
env-gated GE_D252 diagnostic (dims + raw texel dump) for whoever next
gets an interactive session to trigger the actual fire draw.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 19:25:15 -04:00
James Dansereau 8df8ad3c35 docs: adopt cheap GEVR-triage items (crash heuristic, frame-rate notes, process lesson, widescreen pre-flight)
Folds in the zero-code-risk ADOPT items from scratchpad/GEVR-TRIAGE.md
(a prior survey of the sibling GEVR VR port, same n64decomp/007
lineage, reference-only): a crash-triage heuristic for non-canonical
x86-64 pointer faults, two frame-rate-assumption notes that pre-empt a
future uncapped/high-refresh rabbit hole, a process lesson about
grep-built site lists standing in for a real read, and the
portal-scissor site map as pre-flight for whenever widescreen work
resumes.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 19:18:07 -04:00
James Dansereau 65728e9f0e docs(D286): new finding - user-confirmed real freeze on Caverns, Steam Deck
Caught during the same live Deck session used to fix D255: the game's
own hang watchdog (portHeartbeatCheck) fired once, no frame rendered
for ~8s at frames=46404, dumped all-thread state, then rendering
resumed and the session continued to a clean close - not a crash, a
self-recovered stall. User confirmed by direct observation this was a
real, visible freeze (not a Deck-side system hiccup), happening early
in a Caverns playthrough, not tied to an explosion or terminal (rules
out D255/D285 as the cause).

Not root-caused - no distinguishing log line precedes it. Logged as
open with no repro trigger identified rather than guessed at further.

Also fixes a cosmetic units bug spotted while reading the watchdog
code: the heartbeat log printed a raw microsecond delta with a "ms"
label (port/src/libultra.c, no /1000 conversion) - now divided
correctly. No behavior change, log output only.
2026-09-15 18:55:26 -04:00
James Dansereau 7ef0395a3f fix(D255): correct 32-bit pointer truncation in vtxstore_fix_refs
Root-caused via a live Steam Deck session over SSH: sub_GAME_7F09BAC4
(vtxstore_fix_refs) read an active OBJ record's Model* through
((Model*)((ChrRecord*)prop->chr)->chrflags) - a union pun onto
ChrRecord.chrflags, which is typed CHRFLAG, a 4-byte enum. On 32-bit
N64 this is bit-exact (the enum and a pointer are both 4 bytes, so
casting the enum's value to Model* reproduces the real stored bytes).
On 64-bit PC, Model* is 8 bytes but the enum read is still only 4 -
the cast reads just the pointer's low 32 bits as an int and
zero-extends it back to a 64-bit address, discarding the real high
bits. This is the D3x pointer-width ABI class from AGENTS.md - a
struct field misread across the 32-to-64-bit pointer-width transition.

This was actually flagged as suspect (a) in the very first D255
investigation, then wrongly deprioritized in favor of a "destroy-path
race" theory once a register capture happened to show Rdi=(nil) and
looked like it confirmed a different, unrelated NULL-model theory. It
didn't: (nil) is exactly what a 32-bit-truncated read produces
whenever the real pointer's low 32 bits happen to be zero - a
coincidence that pointed two sessions' worth of investigation at the
wrong field. Proof: reading the same record through the correctly
64-bit-typed ObjectRecord.model field (this session's diagnostic
probe) was always a valid, non-NULL pointer - the object's model was
never actually missing.

Fix: read the pointer through ObjectRecord.model (the same union
memory, correctly typed) instead of through the mistyped
ChrRecord.chrflags alias, at both use sites in the function. Identical
bytes and identical result on N64; correct on 64-bit PC.

Verified live on the user's own Steam Deck hardware over SSH, running
their own prepared ROM assets: held through the entire Facility level
including repeated terminal destruction (the most consistent prior
repro) and through Caverns' radio-objective terminals (the second
confirmed level), zero crashes. Windows rebuild + full 21-level
verify.sh sweep also clean.

The separate audio-thread crash (D285, caught during this same live
session) is unrelated and unaffected by this fix.
2026-09-15 18:42:08 -04:00
James Dansereau 49f18ad1e6 docs(D285): new finding - audio-thread SIGSEGV, separate from D255
Caught a real crash on the user's Steam Deck during D255 live-test
setup: a fresh ge007.crash.log from the published v0.2.1 binary,
inside the user's active testing window. Symbolized directly on-device
with addr2line against the crashing binary's own debug symbols.

FAULT ADDR 0x62 inside sndHandleEvent (src/snd.c:366), on the audio
thread (amMain/portThreadWrapper) - not D255's 0x10/vtxstore_fix_refs
on the render thread. Different bug, different subsystem, likely
conflated with D255 in reports since both surface during the same
explosion/combat testing.

Root cause (static, code-read only): sndHandleEvent's pool-exhaustion
preempt-scan walks the shared D_800243E4 ALSoundState list with no
osSetIntMask lock anywhere in the function, while the list's insert
path (sndSetupSound, reached from 15 different game files' direct
sndPlaySfx calls) does lock it - an asymmetric-locking gap matching
the established D4/D147/D152 "N64 interrupts-off isn't a real lock on
PC" race class. Fits the repro pattern (explosion SFX bursts = high
thread-contention window).

Not fixed - flagged two open questions before it should be: whether
the missing lock is a genuine decomp-fidelity gap (N64 assembly may
have had it) vs a legitimate new PC-only lock, and it needs gdb
confirmation on a live crash, not just this static read.
2026-09-15 18:12:30 -04:00
James Dansereau edcfb1f163 docs(D255): consolidate real-Deck evidence - destroy-via-explosion, not activation
Further Steam Deck testing on the published v0.2.1 release reconciles
the two competing D255 repro theories: the trigger is an object dying
via explosion, not terminal activation. Confirmed on four levels:

- Facility: every terminal crashes consistently when destroyed
- Caverns: terminals near the radio objective crash when they explode
- Archives: a similar crash when a guard's grenade detonates (no
  terminal involved at all - narrows the real trigger to "object dies
  via explosion damage" in general)
- Frigate: negative control - shooting a room of terminals there did
  not reproduce it, so it isn't simply "any destructible OBJ record
  exploding" either

Updated the D255 finding with a static lead for the next session (the
objFree destroy path never explicitly nulls ObjectRecord.model, so the
NULL likely comes from a freed-slot-reuse or list-modify-during-iterate
race rather than a direct field write - untested, needs a live Deck
capture with the GE_D255 probes already in tree). Updated
.github/release-notes.md's D255 bullet to match.
2026-09-15 17:58:25 -04:00
James Dansereau b322b55a70 docs(D255): log user's real-hardware Deck report (destroy-trigger, not just activation)
User reproduced on real Steam Deck hardware, v0.2.1 published release:
Facility crashes consistently when terminals are DESTROYED (shot), not
only on activation as the M-139 "corrected" framing concluded. Frigate
crashed once in the same pass, specifics not recorded.

Logging as a dated, evidence-stamped addition rather than overwriting
the existing activation-repro entry - both triggers are plausible
routes into the same underlying destroy-path-ordering race and are not
necessarily contradictory; neither has been cross-verified against the
other yet. Next Linux/Deck session should test both triggers with
GE_D255=1 before revising the "reliable repro" framing again.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 17:35:51 -04:00
James Dansereau 126fe75d7f diag(D255): env-gated NULL-model probe for the Facility terminal crash
Adds a live-repro diagnostic for D255 (Facility door-opening terminal
SIGSEGV, Linux/Steam-Deck-only): vtxstore.c's vtxstore_fix_refs
(sub_GAME_7F09BAC4) skips-and-logs instead of dereferencing when it
finds a NULL model on an active PROP_TYPE_OBJ record, logging the
record's identity/state; propobj.c's objFree logs every free call so
the two logs can be correlated by prop/obj pointer. Both gated behind
GE_D255 (off by default, getenv-cached per the D250 lesson) - zero
behavior change unless set.

Attempted a live Windows repro this session (5 terminal activations
across 3 direct -level_34 launches): zero probe hits, zero crashes.
Combined with the existing WSL-negative result, this is now two
negatives against one Linux/Steam-Deck positive - the bug does not
reproduce on Windows, likely a genuine allocator/timing difference.
Landing the probes anyway since they're inert by default and are the
fastest path to a real trace whenever a Linux/Deck session is
available; no more Windows time should go toward a live repro here.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 17:12:35 -04:00
James Dansereau 0de99b4db4 fix(prng): correct MIPS64 dsll32/dsrl32 semantics in port/src/random.c (D284)
random.c's dsll32/dsrl32 helpers modelled the real MIPS64 dsll32/dsrl32
instructions as ordinary 32-bit shift-by-n operations, masked to 32
bits. Real MIPS64 dsll32/dsrl32 shift the FULL 64-bit register by
n+32 with no masking - they exist specifically to encode a 32-63 shift
amount in a 5-bit immediate field. The bug was bit-exact-looking but
wrong: it produced a deterministic, self-consistent stream that was
never the N64's actual stream, from the very first draw.

Surfaced by M-138's security/decomp-fidelity review (a forked review
agent's numeric trace + a hand trace of random.s both found the same
divergence); fix decided over disclose-as-known-issue per M-139b.

Fix: dsll32(x,n) = x << (n+32), dsrl32(x,n) = x >> (n+32), no masking.
No other line changes - every call site (randomGetNext,
randomGetNextFrom, chrObjRandomGetNext) already mirrors random.s/
chrObjRandom.s line-by-line through these two helpers plus plain <</>>
(which already matched dsll/dsrl correctly).

Verified against a standalone C reproduction outside the game (first
five draws from random.s's real initial seed diverge completely
between the buggy and corrected helpers, confirming the fix actually
changes the output stream). 242/242 clean build; full 21-level
verify.sh sweep PASS, no crashes.

This silently changes every RNG-derived sequence (loot, AI variance,
anything seeded from randomGetNext/randomGetNextFrom) from the first
draw in every build going forward. Savegames are unaffected (seed
isn't save state); existing recorded replays will diverge against a
post-fix build. Called out in v0.3.0 release notes per M-139c.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 16:40:19 -04:00
James Dansereau 532092c732 fix(render): land D266 IA4/texedge decode + D271 portal-culling fixes (M-139 R2)
R2 extraction from the parked D236 investigation tree
(park/d236-probes-m139): pulls out only the two verified, behavior-real
fixes and drops every D267-D280 debug probe (census counters, PPM
dumpers, per-tri/per-portal logging) that had accumulated alongside them.

- D266: import_texture_ia4 decodes RDP IA4 as I2:A2 (was I3:A1, an
  inherited PD assumption); gfx_opengl.cpp's CVG_X_ALPHA "texedge" path
  now keys off the real alphacompare field (dither/threshold/none)
  instead of a hardcoded 0.19 cutoff, and gfx_pc.cpp excludes AC_DECAL
  tiles from the texedge path entirely (it never discards on real
  hardware).
- D271: the D106 near-plane portal-straddling guard's `lim` constant
  widens from 1e5 to 1e38 so finite out-of-range bounds flow through to
  bgRectIntersect and clamp/cull exactly as they do on N64, instead of
  being force-mapped to full-screen (was drawing whole rooms N64 culls).

Checked D271 against D249 (distant-geometry drop-out on Streets/Egyptian)
before landing per M-139's overlap concern: no overlap, different code
paths (near-plane portal guard vs. far-plane/LOD symptom); D249 stays
open.

Verified: 242/242 clean build; full 21-level verify.sh sweep clean (one
dam NO-FRAMES on the sweep pass reproduced PASS on an immediate solo
re-run - a load-sensitivity flake, not a regression).

The D267-D280 probe code and the rest of the D236 tree-wall
investigation stay parked on park/d236-probes-m139 for whenever that
thread resumes (still no root cause for the tree wall itself - see
D280's negative outcome).

Also documents a build-environment gotcha hit this session: this Bash
tool's shell reports MINGW64 (uname/$MSYSTEM) but is actually
Git-for-Windows Bash, missing cmake/ninja/gcc from PATH until
/c/msys64/mingw64/bin and /c/msys64/usr/bin are prepended explicitly
(docs/porting-notes.md SS E).

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 16:34:28 -04:00
James Dansereau e7fee0d7a4 docs: v0.2.1 known-issues sweep, pre-1.0/roadmap framing, AI disclosure
Batched pass across README/release-notes/Pages (one commit instead of the
scattered per-fix pushes earlier this session, per new commit-cadence
preference):

- New known issues surfaced that weren't documented anywhere: no real
  widescreen (16:9 stretches 4:3 rather than expanding FOV), no controller
  rebinding UI / macOS-ARM builds, culling/LOD dropout on Streets/Egyptian
  (D249), gunshot SFX oddities under sustained fire (D240/D241).
- Fixed a stale claim (in three places) that a wrong-sounding-instrument bug
  was still open; it was fixed in v0.2.0 (D230).
- Softened the D259 "All unlocked" fix to reflect reality: still breaks
  audio/aim if enabled before any save exists; flagged experimental
  (new finding D281).
- Corrected D255's repro to the precise, deterministic trigger found this
  session (Facility's door-opening computer terminals, Linux-wide not
  Deck-specific) instead of the vaguer original combat-crash description.
- Logged D282 (front-end menus need right stick not left on gamepad) and
  D283 (Steam Deck first-run preset skipped by an early Desktop Mode
  launch) with user-facing workarounds.
- Refined D263 (Ourumov/Trevelyan) internally with a position-sensitivity
  caveat; no public bullet since it doesn't block progression.
- Added pre-1.0 framing and a new README Roadmap section (known-issues
  cleanup, PAL/JP, real widescreen, rebinding, macOS/ARM, LAN multiplayer
  explicitly "under consideration, no ETA", plus an explicit "not currently
  planned" list) across README/release-notes/Pages.
- Added an AI-development disclosure (agentic development as a genuine
  study of the process, not just a port) across the same three docs.
- Pinned the release-notes hero GIF to the v0.2.1 tag instead of v0.2.0.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 12:53:57 -04:00
James Dansereau 4b51ded7d1 docs: SEO round 2 (Bing) - demote per-page h1 to h2 site-wide, re-trim meta description, bump README/Pages to v0.2.1
Bing Webmaster re-flagged the site after the first SEO pass (32f193a0):
- Every Jekyll-processed doc page (building.md, dev-process.md, internals.md,
  porting-notes.md, dev/agentic-development.md, plus index.md caught last
  time) opens with its own "# Heading" on top of the Cayman theme's masthead
  <h1 class="project-name">, so all of them emitted two h1 tags, not just the
  homepage. Demoted each page's opening heading to h2 -- anchor slugs are
  unaffected (kramdown ids don't depend on heading level).
- index.md's meta description was 161 chars (1 over Bing's 160 limit) and
  still said v0.2.0; re-trimmed to 133 chars and bumped to v0.2.1.
- README.md and docs/index.md's Status lines/comparison table bumped from
  v0.2.0 to v0.2.1 to match the actual current release.

Verified against a live, cache-busted fetch of the deployed Pages site
(the first Bing report was reading a stale CDN edge cache from before the
32f193a0 deploy, per its own Last-Modified header) that the underlying fix
was already live; this pass addresses what was still genuinely wrong.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 10:04:02 -04:00
James Dansereau ff677da74d fix(ci): regenerate findings-index.csv (stale after D216/D181 edits)
Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 09:36:44 -04:00
James Dansereau 1e11e1a5fd v0.2.1 polish: pull Game.ScreenShakeIntensity from the F10 menu (D181)
User testing found the slider "basically useless" -- viShake() only scales
explosion/effect shake (explosion.c), never the always-on walking head-bob
or a getting-shot reaction, which is what "Screen shake" implies to a
player. Removed the menu row until it covers all screen-shake/view-bob
sources, not just explosions. Config var + fr.c hook untouched.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 09:28:06 -04:00
James Dansereau 99f4f38e1c v0.2.1 polish: pull Game.SkipIntro from the F10 menu (breaks audio)
User spot-check of the v0.2.1 draft found that enabling Skip intro breaks
audio. It was already off by default (D216); removing the menu row so
players can't enable it until the audio interaction is root-caused. The
config var and lv.c hook are untouched (dead unless an existing ini already
has it set).

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 09:18:40 -04:00
James Dansereau 06b675b5e8 fix(ci): revert GE-ENV-PROBES.md rows for uncommitted D236 probes
The v0.2.1 docs commit (572ab7bf) added GE-ENV-PROBES.md rows for the
D266-D280 tree-investigation probes, but that probe code itself was
deliberately left uncommitted (still mid-investigation, unswept). CI's
tools_pc/gen_env_probes.py --check caught the mismatch: 13 documented
vars with no live getenv() site in the committed tree. Reverting the
doc rows to match what's actually committed; they'll return together
with the probe code once the D236 investigation lands.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 08:59:51 -04:00
James Dansereau 572ab7bf5b docs: findings/backlog through D280 (v0.2.1 batch + D236 tree-wall passes 2-9)
Records the v0.2.1 hotfix batch (D259-D264, this commit's code) and the
ongoing D236 Surface-1 tree-wall investigation (D265-D280): still open as of
D280 -- fog math verified N64-faithful, the noise class confirmed as the
weave's painter, the portal-culling over-draw fix (D271) real but
insufficient, no quick win identified. The D266-D280 probe code and the
D266 IA4 decode / portal-culling fixes themselves stay uncommitted pending
the game-wide regression sweep GRAPHICS-BACKLOG.md flags.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 08:21:56 -04:00
James Dansereau de6c875bb8 docs: cut-content research — article bodies recovered, claims verified vs decomp
All six TCRF article bodies were extracted (backlog/cut-content/articles/,
gitignored) and cross-referenced against src/ + assets/obseg/. Full verified
findings with file:line refs land in backlog/cut-content/RESEARCH.md.

Verified 1:1: the 11 unused cheats (CHEAT_ID enum vs cheat.c grant table),
music track index (MUSIC_TRACKS enum vs Notes-page GS index), MP character
table (~40 entries, 1.0446 eye height), all unused text strings (LmiscE/LgunE/
LpropobjE/LtitleE), build dates (compiletime.c), Spectrum emulator plumbing
(front.c MENU_SPECTRUM_EMU — only the transition is missing).

Corrections to TCRF/first pass: bg_dest is shipped Frigate geometry (not an
early version); cryp/dish are shipped Egypt/Temple; Library/Stack share
Basement's bg_ame; 28 of 29 unused items exist in the object table.

Tracked doc updated to point at the research file; backlog/ gitignored.
2026-09-14 01:02:07 -04:00
James Dansereau 395c616fc5 docs: cut-content research backlog from TCRF export (first pass)
Cross-reference the local TCRF 'cutting room floor' export
(scratchpad/backlog-scrape/restore-cut-content-mod, gitignored) against the
decomp source and extracted asset tables. Article bodies were lost to a
Cloudflare challenge at scrape time; findings rest on the ~170 intact article
images + all image filenames, each mapped to ROM object/level evidence:

- A: full debug menu (U+D C-hold) decompiled in debugmenu_handler.c — free cam,
  level switch, ramrom record/replay, screenshot grabs; all compiled into PC build
- B: cut weapons (taser/rocket/grenade/P90/spectre/autoshot) have models + stats
  + SFX; rocket/grenade/P90 grantable via existing cheat codes (cheat.c)
- C: 'unused items' page maps 1:1 to file_resource_table entries with use-logic
- D: 8 commented-out MP map entries in front.c with dev notes (Citadel/Statue/
  Cradle/Aztec/Runway/Dam/Depot/Frigate)
- E: per-level unused props + orphaned level geometry (Crypt has bg+briefing but
  no LEVELID; len/dish/cat/oat/arec/ame/ark/ref dirs beyond shipped set)
- F/G/H: unused faces, prerelease comparison pairs, dev-build video embeds

Feasibility tiers 0-3 with the port-layer-only constraint called out.
2026-09-14 00:48:30 -04:00
James Dansereau e1cb96ef19 v0.2.0 final: AllUnlocked default OFF (D259 audio quirk); raise draw/LOD distance + MSAA defaults 2026-09-14 00:18:59 -04:00
James Dansereau 436484a926 docs: final v0.2.0 pre-tag pass
- Drop-in ROM wording everywhere (README quick start, Pages landing,
  release notes, bundled README): first launch auto-generates the
  derived assets via the frozen converter; no Python/toolchain needed.
  PAL/JP marked not-yet-supported with issue #85 pointers.
- building.md: Linux build is CI-compiled and ships in the bundle
  (was 'never built or run on Linux'); bundle note now describes the
  frozen ge007-convert auto-spawn; PAL/JP sidecar caveat (D258).
- Status rewording across README/Pages/release notes: drop the
  'first open-source port to our knowledge' claim; cutscene glitch
  description aligned to 'mostly with James Bond'; index.md status
  no longer contradicts the open D255 Deck crash.
- Em-dash cleanup across all GitHub Pages docs (index, building,
  internals, porting-notes, dev-process, agentic-development,
  release notes); internal dev logs untouched.
2026-09-13 23:51:27 -04:00