Files
goldeneye-pc-port/docs/dev/V0.4.0-PLAN.md
T

36 KiB
Raw Blame History

v0.4.0 Plan — local working document (NOT pushed; update as items move)

Created 2026-09-21 from a fresh triage of findings-index.csv, GRAPHICS-BACKLOG.md, docs/BACKLOG.md, ROADMAP-1.0.md (M-139 phase order), QOL-INVENTORY.md, and the 2026-09-20 HANDOFF.

Release goal (user-set): fix everything that still makes the port not 1:1 with the N64 original; then improvements outside the OG game, plus stability. Sequencing follows M-139's R → Q → F order, split into four phases below.

Baseline

  • v0.3.0 is shipped (tag on origin at 4827bc86); main is 7 docs-only commits ahead. Branch release/v0.4.0 off main when Phase 1 starts.
  • All 20 solo missions (plus the ending-credits sequence) load/render/crash-free; campaign completable end-to-end (Agent, playtested). Steady 60 fps. Audio done. Saves done.
  • Known-issue list = README "Known issues" section (D303, D245, D308/ D306, D307, D311, D318 disclosure, widescreen-stretch, D249, D240/D241, D257/D259/D281 AllUnlocked caveat). D252 retired (M-188), D75 fixed (M-197, user-confirmed), D236 fixed (M-198, pass 26 — awaiting play-test sign-off).

Triage of every open item (as of this triage)

Bucket A — Fidelity (port diverges from N64 behavior/appearance/sound)

ID Item State / notes
D320 17 aim-hold AI lists in 8 levels carry the as-authored D318 softlock race; Facility ai_19, Control ai_9, Depot ai_12 high/med-high Static sweep done; live probe-verification owed (GE_D318T-style timeline on the 3 lists). If one freezes: generalize d318watchdog.c (parameterize list/offset/chr/entityid), not per-list copies. N64 softlocks permanently — watchdog stays a port-side recovery, disclosed like D318.
D252 Rainbow particles still intermittent on both platforms after the vertex-buffer fix Second cause unrooted; prime suspect import_texture_rgba16 fire-tile width/height from tile line size (32×7 vs 16×14). README known issue.
D245 Water seam between two patterns, moves with view (REOPENED) M-142 probe: tc-shift k flips during ordinary rotation; opt-in GE_D245_FIXEDSHIFT candidate build-verified, needs A/B vs N64 reference before making it default.
D303 Wrong extra long muzzle flash (M16 + others) Investigated — pipeline + data ruled out. Flash path (gunUpdateAndFire → rwmtx[1]/[2], dorottex DLPRIMARY arms) is byte-identical game code; M16's MuzzleFlashExtension=6.0 is the legitimate max stat; Gm16Z flash-node data byte-faithful through d43. Headless runs can't fire weapons (guards don't shoot an idle player), so a TEMP GE_D303=1 probe in gfx_pc.cpp logs every very-tall-thin emitted triangle with full RDP state — blocked on a live firing capture (playtest brief item 17).
D306/D308 Z-fighting: Dam intro/truck wheels + general/broader instances Investigated — real fighting confirmed, port-vs-faithful open. Depth precision is equivalent to N64 (24-bit both sides; GE's RSP writes full 16.16 post-transform coords, not s16), so level-authored coincident geometry is the prime suspect and likely fights on N64 too. TEMP GE_ZF=1 flicker probe in gfx_pc.cpp (recurrence-filtered; GE_ZFBOX region filter) shows sustained oscillation (counts ~1000) in the Dam-intro truck region. Closing it as faithful needs an N64 reference of the same spot; a PC-only mitigation (float depth) would diverge from N64 and needs a rule-2-style decision.
D307 In-level security camera props face backwards on some levels (Bunker) Investigated 2026-09-21 — no port-side bug found. Full orientation path traced (setupCctv → per-frame RotY(unkC8)×unk84); every input verified N64-faithful against the ROM (PcctvZ switch data, boundpads incl. the legitimate load-time room-scale ×1.8542, record config words); all math helpers are byte-identical src/game code. Remaining suspect if it genuinely differs from N64: fast3d handling of the non-orthonormal lens matrix. Blocked on user repro detail (which camera/level + screenshot + ideally N64 compare). GE_D307=1 diagnostic kept in-tree.
D236 Surface 1 billboard trees render as a solid wall of texture FIXED (M-198, pass 26, 425237f9): root cause was a texture-BINDING bug — TEXTURETYPE_DETAIL cards drawn from the detail tile (opaque IA8 noise) instead of the base image; gfx_lod_tile_offset now samples tile 1 when the pair differs (Video.DetailBaseTile, default on). Live-verified Surface 1/2 + Depot; other levels provably untouched. Awaiting user play-test sign-off; probes stripped (0503d335).
D235 Wrong textures after death/restart (level reload), multiple levels Headless verification 2026-09-23 (2a6bc2bb): fix clean on reload; bug NOT reproduced even with the fix off. Needs the real die → mission select → restart A/B (GE_D235_OFF=1).
D288 Silo intro: huge flat untextured triangle sweeps the frame (frames ~562–741 of bare -level_20) Does NOT reproduce on the current build (2026-09-23, 30cf76b8); D271 A/B ruled out as the fixer. Close if the Silo playtest does not see it.
D294 Statue: CCCP monument ground renders solid black, self-recovers in 10–30 s FIXED pending user playtest (2026-09-22). Note: its "3× pool corrupts anim" symptom was D336, so the 2× cap can be revisited.
D291 Dam: texture glitches when the truck drives past Not investigated.
D249 Distant geometry drops out on Streets/Egyptian at default FOV M-142 ruled out far-clip + LOD-multiplier; narrowed to portal/room-visibility BFS. First check overlap with D271's portal-culling fix (branch state, see Phase 0). Needs live screenshot+location.
D195 Control: transparency/alpha-blended surfaces render wrong (glass partitions, holographic displays) FIXED (index: gfx_sp_vertex fix) — plan row was stale (2026-09-23 reconcile).
D197 Character face textures wrap around the head instead of clamping (Silo, intermittent) CLOSED (user playtest v0.2.0-pre) — plan row was stale (2026-09-23 reconcile).
D182(2)/D176(b) File-select background degrades after round-trip (comb/interlacing); Surface tree-line static — shared "texture pitch/shear" defect M-36 Family A; import_texture_* line-size assumption. Distinct from the disproven D183 hypothesis.
D176(a) Sky: RDPHALF tri-raster decode; horizon-band coverage (Defect 2) still open Path B draft in scratchpad/sky.patch, phase-fold fix applied, never visually verified — one -level_22 screenshot owed. Branch land/d176a-sky-perspective-scroll exists (forgejo remote) — audit + port to main.
D75 Front-end logos: Nintendo logo degenerate (tiny orbiting fragment); Rareware filtering wrong FIXED (M-197, user-confirmed live 2026-09-21): D50 sidecar converter dropped embedded texture blobs. Remaining: pal/jpn sidecar regen on ROM-holding machines.
D290 Watch in-game "controller" section shows no control-pad graphic FIXED 2026-09-23 (a5804d10): D264-pattern template read in watchRenderController + 3 pointer-width/raw-Gfx sites. Sibling sweep also found + fixed D331 (shell casings never rendered, 94dff811).
D174/D172 Blood: decals never interactively verified (D120 needs sidecar regen); spray color fixed but owed an eyeball BUNKER1 firefight with regenerated sidecars (d43 && d69 && d88 --regen).
D240/D241 Gunshot SFX cadence drifts; PP7/AK47 goes silent under heavy fire near looping sounds D241 high-confidence = D207 reproduced (check D207 entry status first — index row is stale/ambiguous). Fold into the D207 voice-cap family.
D322 Long-session audio degradation (issue #87, v0.3.0 full campaign): from Silo onward audio worsens; by Caverns/Cradle OST inaudible + SFX "come and go"; restart cures → session-accumulating state OPEN — static triage done (teardown audit clean, mixer stateless, D202 leak class covered only for ownerless count==-1 loops and only validated single-level; D305 already records a pool/list desync). Ranked: voice-pool exhaustion/counter drift > queue starvation > evtq saturation. GE_D322 pool-telemetry probe shipped 2026-09-21; needs a campaign capture with GE_D322=1 GE_D204=1.
D311 (residual) Upstream anim-poison source still open (freeze class guarded) Likely root-caused by D336 (head-bob alias read); headless Caverns clean at 2× pool. Close after one live Caverns combat session.
D258 PAL/JP asset conversion broken at the source-data level Repair path verified, not applied. Region fidelity — NTSC-only users unaffected, but it blocks "faithful in every region" and any JP/PAL ROM use.
D160 (residual) Cutscene reliability PARTIAL (D243 family mostly fixed via M-189/M-190) Residual = whatever the live GE_D160/GE_D243 campaign pass shows.

Bucket B — Stability (crashes/freezes/hangs; not N64-parity per se)

ID Item State / notes
D289 Dam computer-room crash (Deck, one occurrence, never repro'd) Needs a human re-test on a current build (D255+). If it never recurs → close as untracked.
D287 Windows: real, non-self-recovering freeze after ~15–20 min idle + click-back One confirmed occurrence; gfxFrameMsgQ full is a lead. Consider crash-dump wiring on Windows for the next hit.
D286 Deck: ~8 s self-recovering frame freeze on Caverns One confirmed occurrence, no repro trigger.
D175 Brief stutter/hang (door-open on Runway/Surface) Matches known transient-hang classes; reproduce under tools_pc/debug.ps1 idle-box before believing it's new.
D285 (residual) Audio-thread race fix was PARTIAL Re-check the thread-lock state vs D305's NULL guard coverage.
D281 AllUnlocked breaks audio + RMB aim when enabled with zero saves written User-facing mitigation shipped; proper fix = make the feature robust or gate it harder (see Phase 3 decision).
— getenv caching in hot paths (DONE 2026-09-23, d9d7cd35) (post-release cleanup, HANDOFF M-192) D302 fixed one instance; sweep for stragglers. Cheap perf/stability hygiene.
D117 Frame nondeterminism (GE_DETERM attempted, not achieved) Parked as a research item; do not block the release on it.

Bucket C — Improvements / QoL (outside OG game; after fidelity)

ID Item Notes
Widescreen Currently stretched 4:3, not native — README discloses this DONE 2026-09-23 (D334 world d5d9a36a, D335 HUD/front end f62bceaf): native at any aspect, default on, F10 toggle; Rule-2 approved. Owed: user HUD playtest at 16:9/21:9; D335a cosmetic.
D237 F10 overlay: mouse-wheel scroll, more settings, category grouping DONE 2026-09-23 (91bd0f84): F10 category headers + 5 more rows (wheel scroll already existed).
D314/D315 F10 flicker (user-deprioritized) + main-vs-in-game menu scaling D315 deferred from v0.3.0; bigger design question — lands naturally with the overlay work.
D238 Mouse sensitivity rework (master sens, RMB aim sens, Y-gain parity) Partly superseded: aim modes folded (D337, N64 default via PD integration; Centred (PC) opt-in), Aim range PC/N64 (D338). Remaining: sensitivity feel tuning.
D239 Click-to-lock parity: delete always-grab mode 0 + config key Small; closes D192 by deletion.
D226 Player-adjustable HUD scale (ammo counter, pickup text, dialogue) DONE 2026-09-24 (30720354): Game.HudScale 75–150% (user-verified), fast3d rect scaling about anchors, dialogue/bottom width-capped.
Rumble (D224) Wire osMotor* → SDL_GameControllerRumble (PD precedent exists) Deprioritized M-87; pick up if gamepad support is in scope for v0.4.0 — user call.
Per-pad tuning / hot-plug PD incremental hot-plug + remembered seats; deadzone/scale/rumble per pad Medium; real-controller users.
Window polish HiDpi, center window, maximize/default-fullscreen Small P-class wins.
Unlocked FPS Plan parked; premise changed (D248/D250 fixed — default is now a true 60) Phase 0 re-measurement before any work; motion interpolation stays out.

Bucket D — Infra / housekeeping

  • D244 self-hosted CI runner link failure — fix before the release tag so CI can build win+linux bundles (or confirm GitHub runners suffice).
  • Post-release v0.3.0 cleanup (HANDOFF): remove GE_OBJT probes (chraction.c chrSet/UnsetStageFlags, propobj.c GAS line); M-183/M-185 clamp removal pending full-cutscene re-verification; D309 probe decision (keep for v0.4.0 debugging or remove).
  • Working-tree archaeology: uncommitted D236 second-pass state RESOLVED before this plan was written — 532092c7/6807555c (Sep 15) already landed the D266 IA4/texedge decode + D271 portal-culling fixes on main and dropped the D267–D280 probes. Consequence: Phase 1 item 9 (D249) can proceed with the D271 fix in-tree; the D236 tree-wall work continues from D276's oml/fmt=3 lead on top of the landed decode fixes.

Session additions (2026-09-23/24, local)

  • D336 Frigate "everything breaks but the gun" (uninitialised head-bob alias fields, D100 follow-up): FIXED, user-confirmed.
  • D337/D338 aim modes folded + Aim range option: user-approved ("Seems good").
  • D339 #92 low-end perf: GE_PERFSTAT probe; CPU budget measured (~2 ms/frame), GPU the likely bottleneck; needs target hardware.
  • #105 (Enhanced/Remaster water reflection) refers to a feature/graphics-final-stack branch that isn't on origin or local; not v0.4.0 scope. Its "D329" label collides with ours, so watch for that if that work merges.

Phases

Phase 0 — Hygiene (½–1 session; do before any fidelity work)

  1. Branch release/v0.4.0 off main.
  2. Resolve the uncommitted D236/D271 tree state already landed (532092c7).
  3. Remove GE_OBJT probes + playtest-folder .bat; decide D309 probe fate; getenv-cache sweep.
  4. Fix/confirm CI runner (D244) so release tooling works end-to-end.
  5. Carry-over verifications from the v0.3.0 handoff that were owed: Runway AllUnlocked toggle re-test, menu-click spot check (D316/D317 both closed — just confirm no regression on the new tree).

Release gate (independent of the phases below)

D322 (long-session audio degradation, issue #87) is a campaign-breaking defect in shipped v0.3.0 and does not wait for the phase ladder: the GE_D322 probe is already built and smoke-tested — request the campaign capture (GE_D322=1 GE_D204=1) from the reporter now, and run its decision rules as soon as the log lands. If the capture implicates a port-side leak, the fix slots into Phase 2; if it's a rule-2 behavioral difference, it gets its own sign-off item.

Phase 1 — Fidelity wave (the release's core)

Ordered by (crash risk → visibility → effort); each item ends with its own verification (headless where possible, human eyeball/ear as merge gate):

  1. D320 live verification + watchdog generalization — the only open item that can hard-lock progression on 8 levels; probes already exist.
  2. D289 re-test (crash class; one human pass, then close or chase).
  3. Batch "one-off geometry/texture anomalies": D288 + D294 + D291 — all are single-location, bounded, visually documented; likely share a fast3d import/culling root cause. One investigation thread.
  4. D252 rainbow particles second cause — retired (M-188): the user's symptom closed as a duplicate of the D219 fix (live retest clean); decode + dimensions proven correct (M-141/M-156). Re-open only on a fireball-rainbow re-report.
  5. D245 water seam — candidate fix exists; A/B against N64 reference, then flip default (A1-class fidelity call).
  6. D303 + D306/D308 z-fighting family — small visual parity items; batch per commit cadence. (D307 camera flip investigated 2026-09-21: no port-side bug found, all inputs verified N64-faithful — blocked on user repro detail; see Bucket A row.) D303 + D306/D308 both investigated 2026-07-19: no port-side bug found in either (pipeline/data/precision all verified N64-faithful); capture probes GE_D303 and GE_ZF are in-tree — both now blocked on user-side captures (firing repro / N64 reference). Nothing further to do agent-side until those land.
  7. D236 tree wall — done (M-198, pass 26): TEXTURETYPE_DETAIL tile-binding fix in fast3d (Video.DetailBaseTile); live-verified, probes stripped. Only the user play-test sign-off remains.
  8. D235 texture reload leak — done (M-193): port-side TextureCacheMap now cleared on every stage transition via videoResetTextureCache() in boss.c; 21/21 sweep pixel-identical; live death/restart confirm is a playtest item.
  9. D249 culling — after the D271 overlap check; live screenshot+location first, BFS instrumentation second.
  10. Audio parity: D322 long-session degradation first (issue #87 — the only audio report that breaks a full campaign; GE_D322 probe already shipped, get the campaign capture, then apply the decision rules in the finding), then D240/D241 (fold into D207 family) + verify D230's bass fix by ear on the elevator track.
  11. Front-end fidelity batch: D290 watch pad graphic, D182(2)/ D176(b) pitch-shear defect, D176(a) sky Defect 2 (verify the patch visually first — it's been unverified for weeks). (D75 logos fixed M-197; D195 Control alpha FIXED per findings; D197 face wrap CLOSED at v0.2.0-pre playtest — both were stale in this list.)
  12. D174/D172 blood verification (regen sidecars, BUNKER1 firefight).
  13. D258 PAL/JP asset conversion repair — apply the verified repair path; re-run the region sweep. Decide scope: ship JP/PAL-accurate or document NTSC-only for v0.4.0 (user call).
  14. D311 upstream anim-poison source — close out the last open thread of the freeze class so the known-issue bullet can be dropped.

Exit criteria: every README "Known issues" bullet either fixed or explicitly re-disclosed with a v0.5.0 pointer; no new fidelity regressions (21-level sweep after each landing).

Phase 2 — Stability wave

  1. D287/D286 freeze investigation — wire Windows crash dumps (or an in-process fault log) so the next occurrence is diagnosable; pattern-match against gfxFrameMsgQ lead and the D309 teardown-race class.
  2. D175 stutter — idle-box repro under tools_pc/debug.ps1; likely a known transient-hang class, confirm and close or chase.
  3. D285 residual — re-audit audio thread-locking vs D305's guard.
  4. D281 AllUnlocked no-save path — make it robust (root-cause the sndHandleEvent/aim interaction) or gate the toggle until a save exists. User call on which.
  5. Long-idle soak test (both platforms) as the stability acceptance bar.

Phase 3 — Improvements / QoL (only after Phases 1–2 are green)

Order by user-visible value:

  1. Native widescreen (letterbox/center instead of stretch) — the biggest remaining "feels wrong" item; decide the CenterHUD G-class sign-off first.
  2. F10 overlay wave: D237 (wheel + categories + more settings) + D314/D315.
  3. Input feel: D238 sensitivity rework, D239 delete always-grab, window HiDpi/center; rumble + per-pad tuning if gamepad is in scope (user call).
  4. D226 HUD scale (F10 slider candidate alongside FovScale).
  5. Unlocked FPS: re-measure Phase 0 against the post-D248/D250 baseline and decide whether v0.4.0 or v0.5.0.

Phase 4 — Release ritual

  1. ./build-pc.sh ntsc-final clean; /linkcheck; full 21-level tools_pc/verify.sh sweep --json.
  2. Full campaign playtest sign-off (user), incl. the D320-probe levels and a water level + Surface + Silo (the Phase 1 visual fixes).
  3. Refresh README Known Issues, .github/release-notes.md, findings index; regenerate agentic-development.md stats from git (still owed since v0.3.0 prep).
  4. Push release/v0.4.0 → tag v0.4.0 → CI draft bundles → validate both artifacts → fast-forward main.

Explicitly out of scope for v0.4.0

  • macOS/ARM, LAN multiplayer.
  • Motion interpolation / sim-rate changes (UNLOCKED-FPS non-goals).
  • D117 frame determinism (research-parked).
  • Mod support and HD texture packs — both post-1.0, both researched; see below.

Post-1.0, researched and tracked (do not start in v0.4.0)

Both were investigated 2026-09-22 against the Perfect Dark PC port fork perfect-dark-dabs-mod-dabs-mod (MIT, Ryan Dwyer — same license as this tree; AGENTS.md already names the PD port a standing reference, so code may be adapted with attribution). Fuller sequencing notes live in the local docs/dev/notes/MODERN-PORT-FEATURES-BACKLOG.md §5/§6, which is gitignored — the durable record is docs/dev/findings.md and the summary here.

Mod support (ROM patches) — viable, medium–large. Findings D325–D328.

  • Community mods repack the ROM, so vanilla offsets are useless: Goldfinger 64 (12→24 MB, 797/803 slices differ) and even the content-neutral Random Eye randomizer (identical ROM size, yet 803/803 slices differ — content shifted +7392). Both die in tools_pc/d43_emit.py:273 on a vanilla-offset zlib slice.
  • The fix is not a loader rewrite. PD's port/src/modimport.c states our own constraint verbatim and instead diffs the patched ROM against stock, extracting what differs into a mod directory its loader already reads. The game keeps running its stock ROM at fixed compiled offsets.
  • GE's file table is in the ROM after all (D327 said otherwise; corrected by D328): 1172-compressed data segment at ROM 0x21990 — one raw inflate, which port/src/rzdecomp.c already wraps — table at 0x252c4, 727 records of 12 bytes, pointers 0x80020d90-based, size by differencing adjacent addresses, exactly obInit (src/game/ob.c:126). Verified: 681 exact offset+size matches vs filelist.u.csv, 0 mismatches; discovery needs no hardcoded offsets and reads Random Eye's own table to 704/726 files byte-identical.
  • Remaining work: extraction in C (proven), patch decoding (PD's rompatch.c is an MIT VCDIFF/BPS/IPS decoder — removes the GPLv2 xdelta3 constraint), converters taking the extracted table, and the real effort: a mod-directory override layer — port/src/romdata.c reads the ROM only and has no analogue of PD's romfile.moddir file slots.
  • Hard limit: mods that add levels need new stage-table entries in src/game — rule 2, not the ABI/layout exception. Either a sign-off or scope down to the existing 20 slots. ASM/code mods stay impossible (ROM code is never executed here).

HD texture packs — medium, well-scaffolded. Answers issue #100 ("High Resolution Textures", open, enhancement — a user asking whether HD textures can be used with the port). Architecture studied from PD's port/include/texpack.h + port/fast3d/gfx_pc.cpp; no finding written yet, so this entry and backlog §6 are the record.

  • Identity is the whole problem, not decoding. The renderer only ever sees the pointer gDPSetTextureImage was handed, so PD keeps an address → texture-number registry populated where texLoad() runs and invalidates it when the pool drops (a stale entry names the wrong texture). Then three-tier matching: texture number → texel checksum (for textures embedded in model files) → per-record. Packs are textures/<texnum>.png; decode happens on a worker so loading does not hitch; the hook is inside import_texture, gated so it costs one lookup when off.
  • Most scaffolding exists here: our gfx_pc.cpp is the same lineage (gfx_texture_cache, :92,579), g_Textures[].texturenum exists (src/game/chr.c:3894), and GE_TEXDUMP already dumps textures (port/fast3d/gfx_pc.cpp:1205) — it just names them by sequence (r000_f3_s1_64x64.bin) rather than texture number, so dumps cannot round-trip. Renaming that is the single change turning the probe into a pack dumper. A PNG decoder is the one new dependency, and zlib is already required.
  • Step 1 (the identity registry) is the only novel design decision, and it records identity where textures load — a src/game touch needing #ifdef PORT plus a §F note. Settle that at design time, not mid-implementation.

Session log (agent-driven, 2026-09-20 evening)

Phase 0 — done except user-side items:

  • release/v0.4.0 cut off main (which already carried the D266/D271 fixes, 532092c7 — the "uncommitted tree state" item was stale; plan updated).
  • D322 probe work committed (7bb43260); GE_OBJT probes removed (D321 closed, M-192 item 7, 56ac2ab8); D309 probe kept (inert, cheap, recurrence value for the D309/D310 freeze class).
  • getenv hot-path sweep (D302 follow-up): function-level scan of all GE_* sites found no frame/triangle-rate stragglers in fast3d; cached four per-frame/per-hit guards in src/game/bg.c (GE_D104 room pass, GE_D63 primary+secondary render logs, GE_D154 inner GDL walk) — all #ifdef PORT, mechanical.
  • D244 (self-hosted CI runner): needs pacman -Syu on the box itself — user-side action, not locally fixable; fallback (#ifdef PORT thin wrapper for the #pragma weak alias) only if the toolchain update fails.
  • 21-level sweep gate: running on the new tree (see scratchpad/sweeps/).
  • Carry-over D316/D317 + Runway AllUnlocked re-tests are interactive → folded into the big playtest brief below.

Phase 1 — in flight:

  • Item 1 (D320): generic pin-signature detector added to d318TimelineTick (GE_D318T=1 now covers any chr: ACT_ATTACK + live-fire attacktype + byte-stable animframe for 10 s → one D320T: PIN? line per chr, repeating every 10 s while pinned). Detection only — the recovery watchdog stays hardcoded to ai_22 per D320's own instruction until a second list freezes. Live verification of Facility ai_19 / Control ai_9 / Depot ai_12 beats = playtest work (see brief).
  • Item 3 (D288/D294/D291 batch): D288 probe refined per M-155 follow-up (any_behind_camera==0 only — the w<0 bbox math was a false lead; oml word now logged for asset ID). D294: GE_D104 room-visibility trace confirmed sufficient (curRoom/roomsDrawn/budget + per-room load state every 10 s); needs live Statue repro at FovScale≈130 + max draw distance. D291: still needs live Dam truck details — lowest value of the three.
  • Item 13 (D258 PAL/JP): deferred pending user scope call (ship multi-region or document NTSC-only for v0.4.0) — no PAL/JP ROMs available here to verify a blind CSV rename against.
  • Item 4 (D235 wrong textures after death/restart): mechanism found + fix applied (M-193). M-108's refutation of the game-side CI-cache theory stands, but the port has its own TextureCacheMap (source-address keyed, no content check) that was never invalidated on stage transitions — videoResetTextureCache() existed with zero callers. Now wired into the per-stage reset block in boss.c (#ifdef PORT, after memaReset). Pixel-identical re-uploads; golden sweep running to confirm. Live death/restart confirmation + a GE_D235 >0 capture are playtest items.
  • D311 upstream poison source (M-194): static pass done — all sub_GAME_7F06FE90 callers pass arg2=16.0f (denominator safe); the old→new lerp only circulates values; bheadAdjustAnimation is the one path where a live player-velocity corruption reaches model->speed. Both speed-write choke points (modelSetAnimSpeed, modelSetAnimation2) now log D311: … ANOMALY on |v|>1e4/NaN — next Caverns occurrence is a one-line diagnosis. No env var (D156-guard pattern).
  • Item 4 (D252 rainbow particles): effectively closed — no agent work left. Re-read the finding this session: M-141 refuted the width/height suspect (line=4 → correct 16×14, cross-confirmed by the GE_TEXDUMP is_fire_shape filter), M-156 proved the ROM bytes decode to a clean white-hot→ember progression, and M-188 closed the user's actual symptom (lingering residue sprites) as a duplicate of the D219 fix — user retested live: "seems fine now." Only a fireball rainbow re-report would re-open it. Plan item 4 retired.
  • Item 10 (D207 alarm klaxon): blocked on user repro info, not agent- workable. M-77b implemented the designed last-resort LOOPED-preemption pass + [D207-DROP]/[D207-YIELD] counters, BUNKER1 forced-alarm soak showed zero drops / zero yields (pass 1 always finds a victim), and the change was reverted. The finding now asks for: which level, which weapon/situation, and a GE_AUDIOTRACE=1 capture (or clip) of the failing alarm — then decide between heavy-level 8/8 starvation (re-apply the reverted pass), an alarm-stop-path leak, or pure perception (klaxon mix volume vs N64). Added to the playtest brief as a low-effort ask.
  • Full 21-level golden sweep: 21/21 PASS on the final tree (bb508e5c), confirming D235 fix + D311 probe + all Phase 0 changes are pixel-identical.
  • D75 (Nintendo logo) investigation — M-195, no root cause yet but two suspects eliminated. (a) Fresh GE_D63 run: all 23 logo Primary GDLs emitted every frame, zero unknown-opcode warnings → emission + DL consumption fine; PC GDLs byte-faithful to N64 source. (b) New GE_D75V per-batch vertex probe: transformed coords match the wordmark geometry exactly (MP translation matches the lookat) → basematrix / f32→s32 / matrix-list suspects eliminated. Leading hypothesis: every G_VTX batch encodes v0=0, so multi-batch nodes overwrite shared vertex slots 0..n−1 (only the last batch survives; higher-indexed triangles read stale slots) — consistent with the single tiny orbiting fragment observed. Next: ROM-dump one node's GDL stream and cross-reference G_TRI4 index ranges against per-batch counts; fix would be port-layer fast3d slot management. Probe code (gfx_pc.cpp, videoGetFrameCount() in video.c/h) uncommitted at end of session — commit with the doc updates.
  • D307 (CCTV facing backwards) investigated — no port-side bug found. Env-gated GE_D307=1 probe in setupCctv dumps every input + derived orientation per record. Bunker1 (UsetupsevbunkerZ) decode vs live: model switch offset = PcctvZ (NOT GcameraZ, that's the gun) sw[0] bit-for-bit; boundpads match the pccg sidecar once the legitimate load-time room-scale (pad->pos *= get_room_data_float2(), ≈1.8542 here) is applied; record config words (incl. unkCC) match; all matrix/atan2 helpers are byte-identical src/game code. Conclusion: PC orientation values == what the N64 computes; if the symptom is real vs N64, the remaining suspect is fast3d's handling of the non-orthonormal lens matrix (unk84 row 0 scaled 0.1×). Blocked on user repro detail (which camera/level + screenshot). Findings entry updated; probe kept in-tree (D60/D88 pattern).
  • D303 (extra long muzzle flash) investigated — no port-side bug found yet, blocked on live capture. Mapped the full flash pipeline (gunUpdateAndFire builds rwmtx[1] = yaw×scale×flashext and rwmtx[2] from Switches[2]; the flare arms are dorottex DLPRIMARY nodes) — all byte-identical game code, matrix helpers verified index-for-index. Ruled out: weapon stats (M16's flashext=6.0 is the legitimate max, sniperrifle matches; the ext≥3 ⊆ CAN_DUAL_WIELD overlap is design coincidence) and model conversion (Gm16Z DLPRIMARY records + vertex blocks byte-faithful per d43 round-trip). Added a TEMP GE_D303=1 probe in gfx_pc.cpp (cached-getenv; logs very-tall-thin emitted triangles with blend/oml/comb/prim/env/w) — 75 s unattended runs on Bunker1/Dam/Depot show only static scene geometry, so the artifact needs a live firing repro. Next: user fires the M16 under GE_D303=1 and shares the log lines; the spurious quad's RDP state will identify its source.
  • D306/D308 (z-fighting family) investigated — real fighting confirmed, cause framed. Verified GE's RSP writes post-transform x/y/z/w as full 32-bit 16.16 (not s16 8.8), so the port's float pipeline + GL_DEPTH24_STENCIL8 matches N64 depth precision — no port-side precision deficit exists. Built a TEMP GE_ZF=1 flicker probe in gfx_pc.cpp (96×54 screen-cell grid, per-cell depth history, requires non-monotonic a-b-a oscillation within GE_ZFTOL recurring ≥5× at the same cell; GE_ZFBOX region filter) — earlier center-pair and simple flicker variants saturated on tessellation noise. Dam (-level_33) unattended: sustained hits in the lower-left truck region with per-cell oscillation counts to ~970 → D306's "transparent areas" are real depth-test alternation. Prime suspect is level-authored coincident geometry (fights on N64 too); confirming port-only needs an N64 reference of the same spot, and a mitigation (e.g. float depth) would diverge from N64 — parked pending that.
  • Plan/doc refresh: D75 (M-197) and D236 (M-198) marked fixed throughout (Bucket A rows, Phase 1 items 6/7/11, playtest brief); findings index regenerated; README Known Issues to match.

Big playtest brief (for the user, when the tree is final)

Build: ./build-pc.sh ntsc-final. Set GE_D322=1 GE_D204=1 for the whole campaign (D322 pool telemetry — the release gate). For the three D320 beats, add GE_D318T=1 on: Facility (the captive-scientists monologue/execution beat), Control (the ai_9 guard beat), Depot (the execution beat) — watch for D320T: PIN? lines; a frozen run keeps printing every 10 s.

Checklist beyond the normal play:

  1. D322: full campaign, share ge007.log after (decision rules in the finding).
  2. D316/D317 regression confirm: click menu rows at a few window sizes, esp. low rows (crop overscan bars) with SafeAreaCrop on/off; toggle Game.AllUnlocked back and forth in-game.
  3. D289: Dam computer room — interact with the terminals (was a one-off crash, likely D255-class, already fixed — confirm it stays gone).
  4. D294: Statue, set FovScale ≈130 + max DrawDistance/LodDistance, walk past the CCCP monument at ground level; if the black-ground window hits, note the time (a GE_D104=1 run there is the follow-up capture).
  5. D291: Dam — watch for texture glitches as the truck drives past; screenshot + which surface.
  6. D175: door-open on Runway/Surface — note any stutter/hang.
  7. D287: leave a level idle+unfocused 15–20 min, click back (the non-self-recovering freeze; one prior occurrence).
  8. D320 beats as above with GE_D318T=1.
  9. Audio by-ear: elevator track bass (D230 verify), PP7/AK47 under heavy fire near looping sounds (D240/D241).
  10. D235: on any level, die and restart (or reload a save) in the same session, then look around — textures should be correct (no swapped/wrong stage textures). Run once with GE_D235=1 and share the log if anything looks off (a >0 count means a stale-cache hit slipped through).
  11. D288 (Silo): play Silo normally; if you see the wrong-color target / geometry anomaly, set GE_D288=1 GE_PCDUMP=1 for a 30–60 s run around it and share log + captures (the probe logs the offending oml word for asset ID).
  12. D311 (Caverns): play Caverns ~2 min of normal combat; if the anim- speed freeze/poison recurs, the log will now carry a one-line D311: … ANOMALY naming the corrupting call site — share it.
  13. D207 (alarm klaxon): if gunfire goes quiet during an alarm, note the level + weapon + situation; ideally re-run that spot with GE_AUDIOTRACE=1 and share the log (or a short clip).
  14. D75 (front-end logos): confirm the Nintendo/Rareware logos + legal page still render correctly (fixed M-197; user-confirmed once already).
  15. D236 (Surface trees): Surface 1/2 — confirm the treeline renders as discrete tree sprites, not a wall (fixed M-198; probes stripped).
  16. D307 (security cameras): if any in-level CCTV camera looks like it's facing backwards, note which level + which camera (screenshot) — the orientation math is verified N64-faithful, so a concrete repro is what's needed to keep chasing it.
  17. D303 (extra long muzzle flash): run with GE_D303=1 in your terminal, fire the M16 (and any other gun that shows the extra upward flash) until you see it, then share the D303: log lines from around that moment — each line carries the RDP state of a candidate quad and will identify the spurious draw call's source.

Conventions reminder

  • One findings entry per investigation; next labels after D322.
  • Fidelity fixes: port-layer or documented A1/ABI exception only; genuine behavioral differences need the rule-2 sign-off (docs/dev-process.md).
  • Every subagent reads docs/porting-notes.md first and appends to it.