From ce8427cff2ed7ac8bd9a2475cc1b4c3afacfb2da Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Wed, 16 Sep 2026 16:47:31 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20D243=20M-154=20=E2=80=94=20user=20confi?= =?UTF-8?q?rmed=20double=20Bond=20(one=20frozen=20copy=20per=20shot);=20co?= =?UTF-8?q?rrected=20log=20reading;=20census=20probe=20documented?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - findings.md ## D243: M-154 entry. User symptom sharpened (one new frozen Bond per camera shot; old copies persist, never animate). Corrects M-153: the POSENT window has three phases and in phase 3 prop->pos ITSELF cycles period-4 through three z-lanes while y falls smoothly — the rendered model tracks it (per-lane pose offsets), so it teleports every frame rather than freezing. H1 vs H2 named; probe design + smoke results recorded. §F table cell updated. - GE-ENV-PROBES.md: GE_D243M row updated (chrdraw census, new tick fields, dedupe note). HANDOFF.md (gitignored) got the matching LATEST block with the re-capture command. --- docs/dev/GE-ENV-PROBES.md | 2 +- docs/dev/findings.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/dev/GE-ENV-PROBES.md b/docs/dev/GE-ENV-PROBES.md index 6e7d806d..42c107d1 100644 --- a/docs/dev/GE-ENV-PROBES.md +++ b/docs/dev/GE-ENV-PROBES.md @@ -45,7 +45,7 @@ Two classes: | `GE_D160` | `src/boss.c:744`, `src/game/bondview2.c:791`, `src/aicommands.def:10055,10181,10236,10242,10247` (all `#ifdef PORT`) | Dam exit-cutscene (D148/D160) diagnostic: trace points in `bossReturnTitleStage`, `bondviewSetCameraMode`, and AI cmds `EndLevel`/`exit_level`/`CameraLookAtBondFromPad`/`CameraSwitch`. **Investigation in progress** — user owes a `GE_D160=1` Dam run. | **live** | | `GE_D243` | `src/game/front.c` (`menu_init`, cached `s_d243`) | Per-menu-transition trace with a local per-tick frame counter (D243 stale-DL-burst screen identification). Counter is NOT numerically identical to the port's internal frame count — cross-reference by elapsed offset. | **live** (D243) | | `GE_D243CAM` | `src/game/bondview2.c` (`bondviewFrozenCameraTick`, three `CAMERAMODE_POSEND` branches) | Dumps eye/look-at positions (+theta/verta on the `gBondViewCutscene` branch, +dt/angle/pad on the orbit branch) per call. **Found M-143**: the `gBondViewCutscene` branch fires and its look-at (`field_3C4/8/C`) sweeps through a filter catch-up after the cutscene warp — the shake mechanism. | **live** (D243) | -| `GE_D243M` | `src/game/bondview2.c` (`solo_char_load`, `bondviewRemovePlayerBody`, `playerTick`; cached `d243mEnabled`) | M-151 model-lifecycle probe for the Dam-abseil "two Bonds + shake": `D243M: CREATE/REMOVE` at both `bodyModel` sites, `D243M: tick` (idx/model/cam modes/`prop->pos`/`field_488.pos`/`field_3B8`/`field_3C4-8-C`) while the model exists or POSEND/INTRO is active, `D243M: draw` per on-screen render submission (two lines same frame = second Bond instance). Note: `field_3B8` x/z read at 10× world units in the smoke capture — not plain world position. | **live** (D243) | +| `GE_D243M` | `src/game/bondview2.c` (`solo_char_load`, `bondviewRemovePlayerBody`, `playerTick`; cached `d243mEnabled`) + `src/game/objecthandler.c` (`drawjointlist`, both region variants) | M-151/M-154 probe for the Dam-abseil "frozen Bonds + shake": `D243M: CREATE/REMOVE` at both `bodyModel` sites; `D243M: tick` (idx/model/**propptr/chrptr**/cam modes/`prop->pos`/`field_488.pos`/`field_3B8`/`field_3C4-8-C`/**rp** = render_pos pointer/**raw** = pre-view root translation) while the model exists or POSEND/INTRO is active; `D243M: draw` per on-screen player render submission; `D243M: chrdraw` — M-154 per-frame model-draw census, one line per CHR render submission (Model* + raw root translation) while a scripted camera mode (POSEND/INTRO/SWIRL/FADESWIRL) is active; a second Bond instance = extra lines with *distinct* model pointers at the same frame (same model is drawn twice/frame — two passes — dedupe by (frame, model)). Silent during FPS play. Note: `field_3B8` x/z read at 10× world units in the smoke capture — not plain world position. | **live** (D243) | | `GE_D193` | `src/game/frametiming.c` (`waitForNextFrame`, `#ifdef PORT`) | Per-wall-second line: `render=` rendered frames, `sim=` sim ticks delivered (Σ of the `deltaFrames`/`g_ClockTimer` fed downstream), `clamped=` frames whose catch-up hit `FRAMETIMING_PORT_MAX_CATCHUP`, `maxraw=` largest pre-clamp delta. Healthy steady state = `render=30 sim=60 clamped=0 maxraw=2` (measured M-80, all levels idle). Use to check whether the wall-clock timer under-advances under real AI load (D193). Cheap; safe for a full playtest. | **live** (D193) | | `GE_D193A` | `src/game/chr.c` (`chrTick` → `d193aSample`, plus `d193aMoved` in `chrUpdateAnim`; `#ifdef PORT`) | Per-wall-second per-ticked-chr locomotion telemetry (D193/D209): `chr=` chrnum, `act=` ACT_TYPE, `speed=` world-units travelled/s, `gtd=` g_GlobalTimerDelta, `tier=` `act_gopos.unk59` SPEED tier (0 walk / 1 run / 2 sprint; `-1` when not ACT_GOPOS), `gspd=` `act_gopos.speed` (**a turn rate, not a travel rate** — 0 means running straight), `typ=` PROP_TYPE (3=CHR, 6=VIEWER), `tick=`/`move=`/`frz=` chrTick entries vs `chrUpdateAnim` calls vs freeze-gated (move « tick = the chr is being visibility-culled out of its own locomotion; `move=0` at high `speed` = WAYMODE_MAGIC), `hid=` CHRFLAG_HIDDEN, then the root-motion scalars `scale` / `xlscale` / `animrate` / `playspeed` / `mspeed` (model->speed) / `endframe` / `anim` ptr / `aidx=` that pointer reverse-resolved to its `animation_table_ptrs1` index (40 walking, 41 sprinting, 42 running, 107 walking_unarmed — map via `src/game/initanitable.c`). `GE_D193A_CHR=` restricts to one chrnum. **This probe found D209**: Cradle showed `tier=1` (RUN) with `aidx=40/107` (walk anims) on every sample. | **live** (D193/D209) | | `GE_D193B` | `src/game/model.c` (`sub_GAME_7F06D3F4`, `#ifdef PORT`) | Rate-limited (1/97) raw anim root-motion decode trace (D193): `joint=` / `flip=` / `base=` mtx channel index / `frame=` / `tmp=` decoded s16 translation triple from the anim bitstream / `angle=` / `anim` ptr / `bd=`+`bs=` the D32 u32 bitDescriptors/bitStream addresses / `njoints`. M-80 Facility: `base=0`, `angle=0` **always** (root-motion facing delta never non-zero — top lead), `tmp` ≈ (±small, ~1086 hip-height, ±small). Compare `tmp`/`angle` magnitudes against a clean N64 decomp build of the same anim+frame. | **live** (D193) | diff --git a/docs/dev/findings.md b/docs/dev/findings.md index 95fe1ad4..2212ad3e 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -546,7 +546,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D240 | **Player gunshot SFX timing/cadence reportedly does not match the N64 original's rate (M-106, user QA report).** — full `## D240` entry at file tail | **OPEN — not root-caused; static call-site tracing only, no runtime verification possible in this environment.** Confidence LOW-MEDIUM that this is a *new* bug distinct from the D155/D186/D193/D204/D209 timing-keystone family (all of which gate the identical `deltaFrames→speedgraphframes→g_ClockTimer→g_GlobalTimer` chain this SFX trigger consumes).… | | D241 | **Player gunshot SFX goes silent under some conditions — reported on Bunker with the alarm sounding, "or some other factor" — but only for PP7 and AK47, Klobb…** — full `## D241` entry at file tail | **OPEN — not newly root-caused; high confidence this is D207 reproduced, not an independent bug.** Recommend folding this repro into D207 (or closing D241 as a duplicate once D207 is reopened) rather than tracking separately.… | | D242 | **Player-death sequence UX regression vs N64 original: PC requires an LMB click to proceed to the mission-failed/file-select screen; user recalls N64 auto-plays…** — full `## D242` entry at file tail | **NOT A PORT-LOGIC BUG as far as static analysis can tell.** Confidence HIGH that the LMB-click skip path is faithful original behavior (Z_TRIG-mapped, exists unmodified in `bondview2.c`), not a defect to remove.… | -| D243 | **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle…** — full `## D243` entry at file tail | OPEN — M-144: CONFIRMED PC-only (user, direct N64 comparison) — real N64 does not show this shake. Mechanism (M-143): the `gBondViewCutscene` look-at branch reads `field_3C4/8/C`, a leaky-integrator low-pass filter of Bond's real position that isn't reset when the scripted cutscene warps him, producing a ~1s look-at snap. Filter math is decomp-exact and `g_ClockTimer`-scaled, so the bug must be upstream: either the position warp itself is wrong/bigger on PC, or a call-ordering desync around the `CAMERAMODE_INTRO` transition. M-153: live `GE_D243M` capture (full Dam → abseil; `scratch/d243/m153_dam_abseil_capture.log`) REFUTES the double-instance hypothesis (single model, one draw/frame, no remove/recreate window) and shows the real divergence: the model's rendered position (`field_488.pos` = render readback) freezes on dam-surface points then locks into a strict period-4 cycle through four fixed world points, while `prop->pos` executes the smooth scripted void sweep — the lagged filter (`field_3C4`) chasing that cycle is exactly the observed shake. Next: code dive into `chrTick`→`render_pos` for what produces the four fixed points; D173/D292 are the same family. D146 still fully decoupled (zero hits, 4 runs). | +| D243 | **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle…** — full `## D243` entry at file tail | OPEN — M-144: CONFIRMED PC-only (user, direct N64 comparison) — real N64 does not show this shake. Mechanism (M-143): the `gBondViewCutscene` look-at branch reads `field_3C4/8/C`, a leaky-integrator low-pass filter of Bond's real position that isn't reset when the scripted cutscene warps him, producing a ~1s look-at snap. Filter math is decomp-exact and `g_ClockTimer`-scaled, so the bug must be upstream: either the position warp itself is wrong/bigger on PC, or a call-ordering desync around the `CAMERAMODE_INTRO` transition. M-153: live `GE_D243M` capture (full Dam → abseil; `scratch/d243/m153_dam_abseil_capture.log`) REFUTES the double-instance hypothesis (single model, one draw/frame, no remove/recreate window) and shows the real divergence: the model's rendered position (`field_488.pos` = render readback) freezes on dam-surface points then locks into a strict period-4 cycle through four fixed world points, while `prop->pos` executes the smooth scripted void sweep — the lagged filter (`field_3C4`) chasing that cycle is exactly the observed shake. Next: code dive into `chrTick`→`render_pos` for what produces the four fixed points; D173/D292 are the same family. D146 still fully decoupled (zero hits, 4 runs). M-154: user sharpened the symptom (one new frozen Bond per shot; old copies persist); log re-read corrects M-153 — in phase 3 `prop->pos` itself cycles A,A,B,C through three z-lanes while y falls smoothly, and the rendered model tracks it (per-lane pose offsets), so it teleports every frame rather than freezing; H1 (multiple Model instances) vs H2 (framebuffer accumulation / subcam ghosts) now discriminated by a new `D243M: chrdraw` per-frame model-draw census in `drawjointlist` + extended tick fields (propptr/chrptr/rp/raw); probe smoke-tested, awaiting re-capture. | | D245 | **IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) — "looks almost exactly like skybox" (M-116, user…** — full `## D245` entry at file tail | OPEN — REOPENED, M-142 headless probe found the tc-shift `k` measurably flips during ordinary view rotation (new root-cause evidence); an opt-in `GE_D245_FIXEDSHIFT` candidate is build-verified but needs a live A/B, not shipped as default.… | | D246 | **Tiny pixel strips at the left and right edges of the screen, "like overscan" — look like raw pixels, not 3D graphics (M-116, user QA report).** — full `## D246` entry at file tail | **OPEN — one candidate ruled out (M-116 follow-up), root cause not found.** Step (1) from the original plan is done… | | D247 | **Black bars top and bottom of screen — possible aspect-ratio defect (M-116, user QA report).** — full `## D247` entry at file tail | **NOT A BUG — confirmed faithful, unmodified N64 decomp behaviour, no code change made.** `GE_PCDUMP` captures (both against the pre-existing save and a fresh-EEPROM run, to rule out a stale `Screen` option from earlier test sessions) show the exact same inset on Frigate… | @@ -10715,6 +10715,8 @@ crash class C2 (Facility `-level_34`, Runway `-level_35`): `import_texture_i8` A **M-153 (2026-09-16): the live `GE_D243M` capture (full Dam playthrough → abseil; 351 KB log, 2,991 probe lines, preserved at `scratch/d243/m153_dam_abseil_capture.log`) discriminates M-145's three questions — the double-instance hypothesis is REFUTED, and the model's rendered position turns out to be a period-4 cycle through four fixed world points that diverges from the scripted `prop` sweep; the camera shake is now fully explained mechanically.** **(1) There is no second Bond model.** Exactly one (idx, model-pointer) pair for the entire capture — idx=0, single `bodyModel`, exactly one `draw` line per frame. No REMOVE/CREATE window around the abseil: the model is created at tick 4818 **directly at the post-teleport position** (12.6, −23744, −1330) with `cam=7` POSEND + subcam=1 (INTRO) already set (the D160 `bondviewSetCameraMode(7)` line immediately precedes CREATE), and it persists to the last tick — no REMOVE is logged even through mission-complete/title return (the stage-unload path evidently doesn't route idx 0's body through `bondviewRemovePlayerBody`, or removal happens after the final `playerTick`; minor). M-145's double-image candidates — "two live Bond models" and "stale draw of a removed model via a bodyModel==0 re-seed window" — are both refuted on PC. **(2) Two divergent position streams.** (a) `prop->pos` executes the smooth scripted bungee sweep through the void: y −23859 → +175 (tick ~5000) → −10724 (tick 5910, mission end). (b) The model's *actual rendered position* — `field_488.pos`, which under POSEND+bodyModel≠NULL is written **only** by the on-screen render readback (`bondview2.c:10452`; the `pos ← collision_position` copy at ~4773 is skipped in POSEND, and the other filter re-seed site at 8912 is `mp_respawn_handler`, not this path) — behaves as: tick 4819 = true void position (== prop); ticks 4820–4880 frozen at (y=729.7, z≈−3111); ticks 4881–5041 frozen at (y=229.1, z≈−3106); ticks 5042–5045 a transition burst containing one frame that matches `prop` exactly (y=−12874.7); tick 5046→end: a strict **period-4 cycle through four fixed world points** P1(228.7, −3111) P2(229.2, −3107) P3(79.2, −2512) P4(−1214.8, −2221). The model transform is decoupled from the script in all three axes (by tick 5910 `prop.z`=−1797 while the cycle's z-range is −3111…−2221); it is not terrain-following (the same four values repeat exactly for ~800 ticks while x/z drift). **(3) The camera shake closes mechanically.** `field_3C4/8/C` (= 0.1 × leaky integral of `field_488.pos`) starts at ≈ −22832 — the *exact* `pos2.y` start M-143 observed live — sweeps to +229 over ~180 ticks as the filter converges onto the frozen surface point, then tracks the period-4 cycle. The "violent shake/spazz" = a fixed-eye camera whose look-at anchor (i) starts ~20k units displaced and (ii) teleports between four points at 15 Hz. D146 remains fully decoupled (zero hits, fourth consecutive run). **(4) "Two Bonds" — not present as two instances in this capture.** If the user still saw a double image on this run, the leading candidate is stale `render_pos` submissions lingering in the draw queue while the model teleports between the four cycle points (D75 family): P4 sits ~1,400 units below the dam surface ("floating in the air"), P1/P2 are on it ("standing on the dam"), and each copy would be a frozen snapshot → "neither animates correctly." Not verifiable from this log (the draw probe counts submissions — one per frame — not on-screen images). **Open question for the user: did two Bonds appear in *this* run?** **(5) Cross-ref:** D173 (level-start floating, Cradle spin) and D292 (death sinking) are the same "third-person world model transform" family — the period-4 fixed-point cycle is now a concrete shared-mechanism candidate for all three. **Next:** code dive into where the model's render transform gets its translation during POSENT (`chrTick` → `render_pos`, chraction.c/objecthandler): what state produces the four fixed points (a 4-keyframe anim attached to Bond's model on PC only? a pad table?), and why the smooth `prop` sweep never reaches the model transform after spawn. If the double image is a stale-queue artifact, that is a port/render-layer fix (fast3d draw list) — no game-logic change needed. +**M-154 (2026-09-16): user symptom sharpened to "one new frozen Bond per camera shot"; the M-153 log reading is corrected (the rendered model is NOT frozen in phase 3 — `prop->pos` itself cycles); a per-frame model-draw census probe (`D243M: chrdraw`) is implemented and smoke-tested to discriminate the two remaining hypotheses.** **(1) User observation (reproducible every time so far):** during the abseil, one *new frozen* Bond appears with each camera shot — shot 1 standing, shot 2 mid-jump, shot 3 rappelling; the old copies persist and never animate; the camera glitches when the second Bond appears. **(2) Corrected log reading (supersedes M-153's "period-4 cycle through four fixed points").** The POSENT window (frames 4819–5910) has three phases: **P1** (4820–4880): `prop.y` rises −19791→+175 while `f488` stays frozen at (−4.4, 729.7, −3111.6) — the rendered model is decoupled from prop during the ascent; **P2** (4881–5041): both frozen (prop (−4.3, 175.9, −3107.6), f488 (−4.4, 229.1, −3106.7)); **P3** (5042→end): **`prop->pos` itself cycles period-4 through three z-lanes** — A(≈−31xx), A, B(≈−25xx), C(≈−22xx) — while y falls smoothly at ~30 units/frame. `f488` tracks the *same* cycle exactly with per-lane y offsets (A: +229, B: +79, C: −1215 = per-pad model pose), so in P3 the rendered model is **not frozen** — it teleports between three fixed world points every frame, following prop. M-153's "four fixed points" was lane A (two near-identical samples) + B + C. Structural tell: y stays perfectly continuous across lane boundaries while x/z rotate → either one prop whose x/z come from a cycling source (AI-script GOTO loop setting position to different pads one command per frame — cf. the teleport fall-through at chrai.c:4064 where a failed `chrAdjustPosForSpawn` drops into the next command; or keyframe sampling without interpolation), or `pp->prop` rotating among three PropRecords with synchronized y. **(3) Hypotheses for the visible double/triple image:** **H1** multiple Model instances — each shot spawns a frozen Bond, all drawn every frame (the M-145 family, but spawned via a path invisible to the playerTick probe); **H2** single model + framebuffer accumulation — `subcam=1` snapshot buffer never cleared, so the per-frame teleporting model leaves a trail of frozen ghosts at all three lanes; "old copies persist and never animate" fits H2 naturally (ghosts are frozen by construction). M-153's stale-draw-queue candidate is subsumed by H2. **(4) Probe (implemented, smoke-tested).** `D243M: tick` extended with `propptr=`/`chrptr=` (a changing pointer proves prop rotation; constant → cycling x/z source), `rp=` (the render_pos pointer — a change between phases = matrix-buffer reassignment/aliasing), `raw=` (render_pos[0] translation BEFORE the view transform — distinguishes model-matrix cycling from view-matrix cycling). New `D243M: chrdraw` census in `drawjointlist` (both region variants, objecthandler.c): one line per CHR render submission while a scripted camera mode is active (POSEND/INTRO/SWIRL/FADESWIRL), logging Model* + raw root translation. All CHR draws funnel through it (`chrRenderProp`; player path bondview2.c:10883, generic chrprop.c:443). H1 = extra lines with *distinct* model pointers at the same frame; note the same model is drawn twice per frame (two passes) — dedupe by (frame, model). Accessors `d243mProbeActive()`/`d243mGetFrameCounter()` live in bondview2.c (the camera gate is folded into `active()` so objecthandler.c needs no bondview.h include); D250 cached-getenv kept. Smoke: 45 s bare `-level_33` boot → 4,053 chrdraw + 1,066 tick lines, formats verified, silent in FPS play (cam=FP/NONE gate). **(5) Next:** user re-runs Dam→abseil with `GE_D243M=1` (`cmd /c` capture — see HANDOFF); analysis: (a) distinct-Model* count per frame during POSENT → H1 vs H2; (b) propptr/chrptr stability → prop rotation vs cycling source; (c) raw vs f488pos → model-matrix vs view-matrix origin of the cycle. + **Status (from §F table):** **OPEN — M-109 re-analysis of the same session's raw logs corrects the timing/pattern claims above; the D134/pendingGfx race hypothesis is likely wrong.** Confidence MEDIUM on the correction, LOW-MEDIUM on the new lead. Re-reading `data/playtest_d160.log`/`_run2.log` at the `g_framesRendered` level (not just grepping for `D146`) shows the D146 burst fires **~1600-3900 frames (tens of real seconds) *after*** `bossReturnTitleStage`/the `camera_switch(7)` calls, not during them, in **both** runs — "right in the camera-cut window" and "run 1 only" are both wrong as stated above. This also weakens the D134/`pendingGfx` citation: `osSpTaskStartGo` runs `gfx_run()` synchronously and inline (`port/src/libultra.c:1232-1258`), so nothing from the cutscene's own DL submission is still in flight by the time any teardown code runs — the port doesn't have the async-RSP race the hypothesis above assumes. **New lead:** whatever screen renders in that multi-thousand-frame gap after mission-complete is the more likely site — probably `MENU_MISSION_COMPLETE` (`front.c:7267`, `constructor_menu0D_missioncomplete`) or an adjacent briefing/file-select screen in that chain, same general class as D75 (front-end model absence) / D141 (menu bad-pointer bug), not a cutscene-render race. Of the 4 repeating fault addresses, 3 read as literal zero words and only 1 matches the `0xfafbfbfc…` pattern, arguing against a deliberate poison-fill (none found anywhere in `port/`) and for either genuinely-unwritten memory or a decode landing inside unrelated valid data. Next: an env-gated `front.c` menu-transition trace (`MENU_*` id + `g_framesRendered` on every transition after `bossReturnTitleStage`) plus a `GE_PCDUMP` window over frames ~4500-8000 (not the camera-cut window) to name the exact screen/segment. Cross-ref D160 (this supersedes its stale "cutscene skipped" framing with a "cutscene renders corrupted" one, now further corrected to "corruption isn't in the cutscene render at all") and D173 (Cradle's Bond-floating/spinning cutscene bug — same subsystem family, unconfirmed shared root cause). **M-120: the `GE_D243` trace above was BUILT (statically, i.e. written) on branch `investigate/d243-cutscene-race` (`764a1f26`, pushed, not merged) — `src/game/front.c` only, `#ifdef PORT`, logs the target `MENU_*` name + a local per-tick counter on every `menu_init()` state transition. NOT build-verified: two independent sessions (the dispatched subagent, then this session re-verifying in a fresh worktree) both hit the same reproducible failure — `cmake`'s C-compiler ABI check fails with "Cannot create temporary file in C:\Windows\: Permission denied" when gcc is invoked *through* CMake/ninja, even though a bare `gcc -c` invocation from the same shell, same session, succeeds and correctly resolves its temp dir to `%TEMP%`. This is a genuine, reproducible environment issue distinct from the diff's content (static review of the diff found nothing wrong: enum cases all verified to exist in `bondconstants.h`, brace/`#ifdef` balance checked, follows the existing `GE_D160`/`GE_D178` env-gated trace idiom used elsewhere in the same file) — worth a session with a working toolchain investigating the CMake/ninja-vs-bare-gcc environment discrepancy itself as its own item, separate from D243. Next: build `investigate/d243-cutscene-race` on a session where `./build-pc.sh` actually completes, then run the `-level_33` Dam repro with `GE_D243=1` (and the existing `GE_D160`/`GE_PCDUMP` mechanisms) to correlate the trace against the D146 burst's frame range. **M-141: the M-120 toolchain blocker is EXPLAINED and worked around, GE_D243 is now build-verified.** The "Cannot create temporary file in C:\Windows\" failure isn't specific to CMake/ninja vs. bare `gcc -c` — it's this Claude Code session's `Bash` tool shell (Git-for-Windows Bash, not a real MSYS2 MINGW64 window) not exposing a usable `TMP`/`TEMP` to child processes the way an interactive MSYS2 shell does; a bare `gcc -c` from the same shell happened to not hit the temp-file path M-120 tested, which is why it looked clean while the full build wasn't. Fix: explicitly `export TMP=... TEMP=...` (Windows-style path) alongside the already-known `PATH=/c/msys64/mingw64/bin:/c/msys64/usr/bin:$PATH` fix, both before invoking `./build-pc.sh`/`ninja`/`cmake` — now also recorded in `porting-notes.md` §E as a general session-start gotcha, not just a D243-specific note. Cherry-picked the `GE_D243` trace commit (`764a1f26`) cleanly onto `release/v0.3.0` (branch `investigate/d243-fresh`), fixed one thing the original diff got wrong per this project's own D250 rule (`menu_init()` runs ~once/frame during front-end states — the diff's three `getenv("GE_D243")` calls were uncached; now a single cached `static int s_d243` check), and confirmed it builds clean. **Smoke-tested, not fully exercised:** a bare `-level_33` boot runs 1100+ frames with zero crashes and (correctly) zero `D243:` log lines — bare `-level_XX` skips the front end entirely (per this doc's own M-24-era note), so `menu_init()` never runs; this only proves the probe doesn't regress normal boot, not that it captures anything useful yet. **Still blocked on the same thing M-107/M-109/M-120 all were: an actual interactive playthrough** (main menu → Dam → abseil cutscene → mission-complete → whatever screen the D146 burst fires on, ~1600-3900 frames later) with both `GE_D243=1` and `GE_D160=1` set, which needs a human with input, not available in this session. **Possible connection to D173 worth checking in that same playthrough** (per user, M-141): D173 (floating/spinning Bond puppet in cutscenes, e.g. Cradle/Trevelyan) and D243 (stale-memory DL read some seconds after a cutscene-driven `bossReturnTitleStage`) both sit in the scripted-cutscene/level-transition neighborhood and both ultimately touch "Bond's (or a puppet's) in-world model" — no code-level link found this session (D173's own M-106 pass already ruled out the two most likely struct-aliasing sites, and D243's mechanism looks like a stale/freed buffer read in a *different* subsystem, front-end/menu memory rather than a chr/puppet transform), so treat this as an unconfirmed hypothesis to keep in mind while capturing the next live trace, not an established shared root cause. **Steam Deck playtest confirmation, same M-141 session** — user independently reported "cutscene at end of dam level is badly glitched" without prompting, matching this row's own "camera violently shakes" symptom exactly. Second independent confirmation this is real and reproducible, not a one-off; still needs the interactive `GE_D243=1`+`GE_D160=1` capture this row already calls for. ## D244 — Self-hosted CI runner (`corpyvt6agent01`) link failure: `undefined reference to 'objectiveGetStatus_WEAK'` (2026-09-12, first-ever build attempt on the…