Devlog ·
Regtest Harness Worklog — March 28, 2026
~112 min read · 28149 words
Goal
- Replace the old final-frame/hash-based PS1-vs-reference harness with a scene-sequence harness that compares PS1 output against the canonical host baseline corpus in
regtest-references/.
Current status
- Canonical host baseline corpus exists in
regtest-references/for all 63 scenes. - Core sequence comparator has been upgraded to:
- align on scene entry
- separate PS1
pre-scene,in-scene, andpost-sceneframes - report
MATCH,PIXEL_MISMATCH,TIMING_MISMATCH, orALIGNMENT_FAILED
regtest-scene.shcan now request sequence-based baseline analysis against a canonical scene directory.compare-reference-batch.shhas been rewired toward sequence comparison and canonical reference defaults.build-ps1.shnow recreatesbuild-ps1/if cleanup removed it.- Raw PS1 output roots with nested Docker frame directories are now discovered correctly by
compare-sequence-runs.py. regtest-scene.shnow has a--fast-baselinemode for interactive validation runs that skips full telemetry/visual-batch postprocess.- Host capture now stamps engine-truth
scene_start_frame/scene_end_framefrom the executable itself.
Known issues
- Full-frame PS1 telemetry decode is still too expensive for interactive per-frame validation unless
--fast-baselineis used. - First live validation scene (
BUILDING 1) still does not produce a positive PS1-to-host alignment yet. - Old Dockerized regtest outputs can leave root-owned files in result directories; reruns should use fresh output dirs or the harness should isolate/chown them.
- The sequence comparator is still too slow once the PS1 side has thousands of candidate frames; entry-anchor search needs one more optimization pass before it becomes a comfortable interactive loop.
Validated so far
- Canonical host references are present and organized.
- Shell/python syntax for the updated harness scripts is clean.
- The PS1 validation lane now reaches the sequence comparator instead of failing earlier on missing build directories or missing result metadata.
BUILDING 1host reference starts at real scene content;visual_detect.pyis unreliable on host BMP frames and must not be used as the host entry oracle.BUILDING 1PS1story single 10remains in title/ocean flow for a long time; the first scene-like sampled frame in a 4200-frame probe isframe_03660.png.- That same PS1 probe still does not match the canonical
BUILDING 1host baseline byframe_04200; this is a real content mismatch, not just a missing-frames plumbing failure. - A short
story direct 10PS1 validation run still spends hundreds of frames in BIOS startup, so even the direct lane needs a larger boot budget than 600 frames. story directis not an apples-to-apples baseline route.story singlepreserves the normal startup flow, whilestory directbypasses it and adds a 600-frame hold tail by design.- Fresh host
ACTIVITY 1capture with engine markers reportsscene_start_frame = 7,scene_end_frame = 328,frames_captured = 328. So host scene boundaries no longer need to be guessed visually. - A cheap sampled PS1 scan for
ACTIVITY 1shows first non-title content around frame2700, but it isocean, not the host activity scene. - The comparator now fails fast on missing anchors instead of hanging for minutes; remaining speed pain is reduced to long known-fail searches rather than general unusability.
- Fresh host-vs-host
ACTIVITY 1comparison is an exactMATCHwithframe_offset = 0,common_frame_count = 328, andaverage_palette_index_diff_pixels = 0.0. The comparator now has a real positive-control path. - Long sampled PS1 run for
ACTIVITY 1reachesoceanaround frame2700and does not reach any host-matchableACTIVITY 1scene even after enteringislandcontent around frame6300. - Late-window compare against the long sampled PS1 run still reports
missing verified scene_entry anchor, so the PS1 side is eventually showing the wrong island scene, not merely arriving late to the right one.
Next steps
- Trace why PS1
story singleboots are entering the wrong island scene forACTIVITY 1. - Use
ACTIVITY 1as the first live PS1 route/debug target until the requested scene is actually reached. - After that, revalidate
BUILDING 1on the same harness and continue scene-by-scene. - Keep the harness stable and avoid further heuristic scene-entry work unless a new gap appears.
Progress update
- Fast direct validation lane shows PS1 still in BIOS/logo at ~frame 1500 and plain ocean at frames 2000-2400 for
story direct 10. - This strongly suggests a real PS1 scene-routing mismatch for
BUILDING 1, not just harness timing error. - The remaining harness gap is no longer host scene detection; it is PS1 entry-window search and comparator speed over long PS1 captures.
- With host scene markers in place, the next blocker is no longer “when does host scene begin?” but “why does PS1
story singleland in ocean content for scenes likeACTIVITY 1?” - Next debug target after the speed pass: trace PS1 story-route handling for
ACTIVITY 1/BUILDING 1and fix that before deeper pixel work. - Comparator confidence is now materially higher: host capture + host reference can round-trip to an exact match, so the remaining failures are on the PS1 path or in PS1-to-host alignment policy, not in basic compare mechanics.
- Practical conclusion: the harness is now good enough for scene debugging. The current blocker is a real PS1 route/content bug, first visible on
ACTIVITY 1. - Debugging moved into
ACTIVITY 1as the first real PS1 scene bug target. story single 0,story scene 0, and exactstory ads ACTIVITY.ADS 1all hit the same wrong ocean window by frame4200, so the failure is not scene-index mapping.- Narrowing the old random empty-launch ADS retry from all families down to
BUILDING.ADSdid not changeACTIVITY 1behavior. - Added robust current-ADS family/tag markers into the PS1 scene-marker strip so sampled frames can now tell us exactly what PS1 thinks is active during the bad window.
- Added current ADS family/tag telemetry to the PS1 scene-marker strip.
- First marker probe on
ACTIVITY 1showscurrent_ads_family=0/current_ads_tag=0at frames2700and4200, withADD_SCENEseen butlaunched=false. - Long exact boot
story ads ACTIVITY.ADS 1still does not reach hostACTIVITY 1. - Sampled frames
2700and4200remain in failed-launch ocean (launched=false,family=0,tag=0). - By frames
6300and8700, PS1 has transitioned into live island content with Johnny/raft/palm that visual detection classifies as FISHING-like, not ACTIVITY. - So
ACTIVITY.ADS 1on PS1 is not merely late; it eventually turns into the wrong scene family/content. - Added requested-scene and boot-pending markers to the robust PS1 strip to separate story selection bugs from ADS launch bugs.
- Fixed
regtest-scene.shscene-label parsing so scratch labels with spaces no longer corruptADS_NAME/SCENE_TAGor break result summary generation. - Switched requested/current ADS markers to white bit-cells for more robust decode.
- On the
ACTIVITY.ADS 1bitmark probe,boot_pendingstays asserted at frames2700and4200while the run remains in failed-launch ocean. - That rules out early override consumption as the main remaining bug; the failure is downstream of story selection, inside PS1 ADS/TTM launch state.
- Enlarged the isolated black telemetry backdrop for requested/current ADS bit-cell rows so lower rows no longer bleed against live scene pixels.
- Added
ps1AdsDbgZeroIpLaunchestelemetry to distinguish tag-resolution failure from later immediate thread death. - Added
zero_ip_launchto the robust scene-marker decoder and reran an exactstory ads ACTIVITY.ADS 1PS1 probe on the rebuilt image. - Fresh decode at frames
2700and4200shows:launched=falseadd_scene=truetag_hit=truebmp_fail=truesprite_seen=trueboot_pending=1zero_ip_launch=true
- However, authored bytecode inspection shows
ACTIVITY.ADS tag 1starts with valid nonzero launches:slot 1 / GJDIVE.TTM / tag 12 / ip 42slot 1 / GJDIVE.TTM / tag 13 / ip 126
- That means the later
zero_ip_launchsignal in the ocean window is a downstream symptom, not proof that the initialACTIVITY 1launch target is bad. - The next debug question is no longer “does
ACTIVITY.ADS 1target slot 0?” It is “why do the valid initialGJDIVEscene launches fail to survive into a live PS1 scene before the run drifts into unrelated content?” - Added sticky robust-strip bits for the authored
ACTIVITY 1startup launches:activity_init12_launchedactivity_init13_launchedactivity_init12_endedactivity_init13_ended
- Fresh exact PS1 probe on the rebuilt image still reproduces the same failure:
- title/transition noise around
300-600 - black by
900-1200 - wrong-ocean failure window by
2700and4200
- title/transition noise around
- In that bad ocean window, the new strip bits indicate PS1 is at least touching the authored
GJDIVEstartup path rather than a totally unrelated scene family from the outset:activity_init12_launched = trueactivity_init13_launched = falseon the sampled decodeactivity_init12_ended = falseactivity_init13_ended = false
- The decode is still too coarse to treat the exact
12vs13split as final truth, but it is enough to shift the hypothesis again:- PS1 is not simply ignoring
ACTIVITY.ADS 1 - it is entering the
ACTIVITYlaunch path, then collapsing before a stable live scene forms
- PS1 is not simply ignoring
- Next target: instrument per-thread startup failure more directly around
GJDIVE.TTMtags12and13so we can tell whether one of them is never started, immediately terminates, or is superseded by a later bad launch. - Added live robust-strip bits for:
activity_init12_runningactivity_init13_running
- Fresh exact probe with those rows shows:
- at
300and600: both12and13appear as launched/running during startup/title-path noise - at
2700and4200: neither12nor13is running anymore - the bad ocean window still shows:
add_scene=truetag_hit=truezero_ip_launch=truebmp_fail=truesprite_seen=true
- at
- So the current read is stronger now:
- the authored
ACTIVITYstartup TTM threads do exist early - they are gone long before the later wrong-ocean window
- the bad ocean state is a later broken relaunch/fallback condition, not the original
GJDIVE 12/13scene simply staying alive in the wrong form
- the authored
- Next target: identify what later
ADD_SCENE/ relaunch path is taking over after the initialACTIVITYstartup threads disappear. - Added sticky robust-strip bits for later authored
ACTIVITYrelaunch edges:activity_tag7_launchedactivity_tag8_launchedactivity_tag9_launchedactivity_tag11_launchedactivity_tag14_launchedactivity_slot2_tag2_launched
- Fresh exact
story ads ACTIVITY.ADS 1probe to4200with those rows:repo:/regtest-results/activity1-ads1-seed1-relaunchtrace/result.json- At
frame_02700the bad ocean window still shows:launched=falseadd_scene=truetag_hit=truezero_ip_launch=true- none of
activity_tag7/8/9/11/14oractivity_slot2_tag2are set
- Long exact probe to
9000with900-frame sampling:repo:/regtest-results/activity1-ads1-seed1-relaunchtrace-long/result.json- By
frame_06300and still atframe_08100, the later wrong live scene shows:launched=truetag_miss=truezero_ip_launch=trueactivity_tag11_launched=trueactivity_slot2_tag2_launched=trueactivity_tag7/8/9/14=falseactivity_init12_running=trueactivity_init13_running=true
- This narrows the active PS1 bug again:
- the bad ocean window at
2700-4200happens before the later authoredACTIVITYrelaunch chain is touched - by
6300+, the authored relaunch chain has advanced at least as far asACTIVITY tag 11 -> slot 2 tag 2 - the eventual wrong live scene therefore appears during or after that later authored relaunch handoff, not during initial story selection and not during the first
GJDIVE 12/13startup launches
- the bad ocean window at
- Next target: instrument the transition from
ACTIVITY tag 11 / slot2:2into the laterzero_ip_launchlive scene takeover, because that is now the narrowest unexplained edge in the failure. - Added later handoff liveness/end rows:
activity_tag11_runningactivity_slot2_tag2_runningactivity_tag11_endedactivity_slot2_tag2_ended
- Fresh rebuilt long probe:
repo:/regtest-results/activity1-ads1-seed1-handofftrace-long/result.json
- Decoded checkpoints:
frame_02700:add_scene=truetag_hit=truezero_ip_launch=trueactivity_tag11_launched=falseactivity_slot2_tag2_launched=falseactivity_tag11_running=falseactivity_slot2_tag2_running=false
frame_06300andframe_08100:launched=truetag_miss=truezero_ip_launch=trueactivity_tag11_launched=trueactivity_slot2_tag2_launched=trueactivity_tag11_running=falseactivity_slot2_tag2_running=falseactivity_tag11_ended=falseactivity_slot2_tag2_ended=false
- That tightens the read further:
- by the later wrong-scene window, the authored
ACTIVITYrelaunch chain has definitely touchedtag11 -> slot2:2 - but those threads are no longer running there
- and they are not being observed to terminate cleanly through
adsStopScene() - the later wrong live scene therefore looks like a takeover after launched threads disappear without a normal observed stop, while
zero_ip_launchcontinues to accumulate
- by the later wrong-scene window, the authored
- Next target: instrument where launched
ACTIVITY tag11 / slot2:2threads are being invalidated or superseded without a normal stop, and connect that to the continuingzero_ip_launchtakeover path. - Dumped the authored reachable
ACTIVITY.ADS 1chunk graph with the real parser:- entry chunk bookmarks
1:13,1:8,1:7,1:9,1:14,2:2 - direct chunk contents confirm the authored loop:
1:13randomly launches1:8,1:7,1:91:8/7/9can launch1:141:14launches2:22:2relaunches1:13
- entry chunk bookmarks
- Ran a denser transition probe:
repo:/regtest-results/activity1-ads1-seed1-transitiontrace/result.json
- Decoded transition band:
frame_05400throughframe_06000:launched=falseadd_scene=truetag_hit=truezero_ip_launch=trueactivity_tag11_launched=falseactivity_slot2_tag2_launched=false
frame_06150:launched=truetag_miss=truezero_ip_launch=trueactivity_tag11_launched=trueactivity_slot2_tag2_launched=true- neither is running anymore by the time of the sample
- That narrows the failure boundary again:
- the later authored
ACTIVITYrelaunch loop first becomes visible between6000and6150 zero_ip_launchis already active well before that switch- so the zero-IP takeover is not a consequence of the
tag11/slot2:2handoff; it predates it and persists through it
- the later authored
- Next target: instrument the exact launch happening in the
6000-6150window and record the slot/tag/IP of the first live scene that appears oncelaunched=trueflips on. - Found a real decoder bug in
scripts/decode-ps1-bars.py: scene-marker rows were being scaled against448lines instead of native480, which vertically misaligned the robust strip decode on PS1 screenshots. - After fixing the scene-marker scale to
img.height / 480.0, the later handoff frame reads materially differently and more plausibly:frame_06150now decodes as:launched = trueactivity_tag11_running = trueactivity_slot2_tag2_running = trueactivity_live_slot2 = trueactivity_live_ip_zero = trueactivity_live_ip_nonzero = false
- the live-thread tag one-hots for
2,11, and13are all false in that same frame
- Current read after the decoder fix:
- the first visible live thread at the handoff is a
slot 2thread withip == 0 - it is not one of the expected tag one-hots currently instrumented (
2,11,13) - meanwhile the authored
ACTIVITYhandoff threads (tag11andslot2:2) are still marked as running in the same frame
- the first visible live thread at the handoff is a
- This narrows the PS1 bug again:
- the bad takeover at the handoff is now consistent with a live
slot 2zero-IP thread preempting the intended authored loop - the next probe should identify that live
slot 2tag exactly, rather than continuing to infer from the old compact bitcells or noisy emulator logs
- the bad takeover at the handoff is now consistent with a live
On this page
- 2026-03-28 12:59 PDT
- 2026-03-28 14:30 PDT
- 2026-03-28 13:02 PDT
- 2026-03-28 13:31 PDT
- 2026-03-28 13:40 PDT
- 2026-03-28 13:46 PDT
- 2026-03-28 13:51 PDT
- 2026-03-28 13:55 PDT
- 2026-03-28 13:59 PDT
- 2026-03-28 14:03 PDT
- 2026-03-28 14:05 PDT
- 2026-03-28 14:11 PDT
- 2026-03-28 14:48 PDT
- 2026-03-28 15:03 PDT
- 2026-03-28 15:13 PDT
- 2026-03-28 15:34 PDT
- 2026-03-28 15:38 PDT
- 2026-03-28 15:39 PDT
- 2026-03-28 16:08 PDT
- 2026-03-28 16:18 PDT
- 2026-03-28 16:21 PDT
- 2026-03-28 16:30 PDT
- 2026-03-28 16:35 PDT
- 2026-03-28 16:40 PDT
- 2026-03-28 16:45 PDT
- 2026-03-28 16:50 PDT
- 2026-03-28 16:59 PDT
- 2026-03-28 17:02 PDT
- 2026-03-28 17:06 PDT
- 2026-03-28 17:09 PDT
- 2026-03-28 17:15 PDT
- 2026-03-28 17:17 PDT
- 2026-03-28 17:20 PDT
- 2026-03-28 17:22 PDT
- 2026-03-28 17:23 PDT
- 2026-03-28 17:29 PDT
- 2026-03-28 17:36 PDT
- 2026-03-28 17:44 PDT
- 2026-03-28 17:51 PDT
- 2026-03-28 17:57 PDT
- 2026-03-28 18:01 PDT
- 2026-03-28 18:06 PDT
- 2026-03-28 18:11 PDT
- 2026-03-28 18:15 PDT
- 2026-03-28 18:28 PDT
- 18:32 PDT - Post-intro scrub proof changes failure to stable title, not black or ocean
- 18:34 PDT - Replacing first exact ACTIVITY prepare with
adsNoIsland()yields black, not ocean - 18:40 PDT - Skipping
adsPrimeSceneResources()does not change the stable ocean failure - 18:45 PDT - Skipping exact scene island-state calculation perturbs the lead-in, but ocean still settles by
04910 - 02:16 PDT - Keeping the first small pack-header buffer alive did not move
ACTIVITY 1 - 02:18 PDT - Copying
CdlFILEimmediately afterCdSearchFilemovedACTIVITY 1to the third branch - 02:21 PDT - Moving
CdlFILEfully off the local stack also movedACTIVITY 1 - 02:24 PDT - Moving
cdPathoff the local stack also movedACTIVITY 1 - 02:27 PDT - Simple local padding between
cdPathandcdfiledid not moveACTIVITY 1 - 02:31 PDT - Swapping local order of
cdfileandcdPathmovedACTIVITY 1 - 02:36 PDT - A tiny helper around
CdSearchFile(...)also movedACTIVITY 1 - 01:55 PDT - Evaluating the old
ACTIVITY.PAKconditional alone is enough to hold the improved branch - 02:00 PDT - Pure local stack padding moves the run to a third stable branch
- 02:05 PDT - Basic decoded-field bounds hardening does not move the run
- 01:24 PDT - Root-file lookup is healthy before
ACTIVITY.PAKsearch, but not after it - 01:31 PDT - Another pack file resolves correctly before
ACTIVITY.PAKsearch - 01:32 PDT - Literal
\\PACKS\\ACTIVITY.PAK;1lookup fails too - 01:35 PDT - The image has exactly one
ACTIVITY.PAKrecord, and repeated literal lookups stay on the improved branch - 01:40 PDT - A single literal
ACTIVITY.PAKprelookup is not enough - 01:45 PDT - Reusing a known-good literal
CdlFILEstill does not stabilize the normal path - 01:50 PDT - Double literal lookup without the old conditional still falls back
- 00:36 PDT - ACTIVITY ADS metadata is already wrong immediately after
ps1_loadAdsData() - 23:55 PDT - Older
6dae8410base is not a trustworthy post-boot PS1 scene-debug target - 00:05 PDT - Returned to current
HEAD;ACTIVITY 1remains the live scene target - 00:12 PDT - Current
ACTIVITY 1still crosses the old pre-chunk slot-1 corruption seam - 00:18 PDT -
ACTIVITY 1slot-1 corruption is already present by the timeadsLoad()returns - 00:25 PDT -
adsLoad()runtime tag table no longer preserves authored ACTIVITY startup tag12 -> 176 - 00:31 PDT -
ACTIVITY.ADSparse-time metadata is still correct; corruption happens later - 00:39 PDT - The bad size comes from the pilot-pack ADS load path, not from authored pack content
- 00:45 PDT - Runtime
ACTIVITY.PAKindex handling is still the live seam - 00:50 PDT -
ps1PilotActivePack.entriesis already wrong immediately after pack activation - 00:55 PDT - Cold-resetting active/prefetch packs does not recover
ACTIVITY 1 - 01:00 PDT - Fresh
ACTIVITY.PAKheader parse is already wrong on the first 20-byte read - 01:05 PDT - Raw whole-file CD read for the first
ACTIVITY.PAKheader still fails the same way - 01:10 PDT -
CdSearchFilemetadata forACTIVITY.PAKis already wrong before any header bytes are read - 01:16 PDT -
CdSearchFileis not just returning the wrong size; the resolvedACTIVITY.PAKstart sector is wrong too - 01:20 PDT - Resetting CD state immediately before
ACTIVITY.PAKlookup does not recover the scene - 22:58 PDT - ACTIVITY 4 on
6dae8410:story singlewas broken on PS1; now narrowed to exact-scene no-launch - 23:07 PDT - Direct
ads ACTIVITY.ADS 4matchesstory single 4byte-for-byte on key frames - 23:14 PDT -
ACTIVITY.ADS 4is not the same first-chunk zero-thread failure shape asACTIVITY 1 - 23:20 PDT -
ACTIVITY.ADS 4startup-chain proof is also a clean negative - 23:31 PDT - Post-
ttmLoadTtm()slot-2 visual proofs are also negative forACTIVITY.ADS 4 - 23:39 PDT - Even a top-of-
adsPlay(ACTIVITY.ADS, 4)visible return is inert on the captured output - 23:45 PDT - Non-visual validation confirmed: exact
adsPlay(ACTIVITY.ADS, 4)proof does execute even though frames do not move - 23:46 PDT - Fresh-build timeout proof shows
island ads ACTIVITY.ADS 4is not reachingmain()’sargAdsbranch on6dae8410 - 21:54 PDT -
adsLoad(...)heap allocation was corrupting ACTIVITY bootstrap; static ADS tag storage removes the old ocean failure - 22:36 PDT - Step-back validation:
6dae8410is a materially better PS1 base than current HEAD forACTIVITY 4 - 21:20 PDT - ACTIVITY startup failure is now narrowed to missing slot-1 resource binding before first chunk playback
- 21:35 PDT - Raw
RESOURCE.001parse confirms ACTIVITY source asset is correct - 21:45 PDT - First exact ACTIVITY in-memory ADS list and slot-1 TTM load are both correct
- 21:55 PDT -
adsLoad(...)is the active boundary, but not via simple tag/chunk table overflow - 20:16 PDT - Quadrant-color upload proof collapsed the late ACTIVITY frames to near-black, but did not identify tile ownership
- 20:28 PDT - By
04900..05000, no normal background-tile uploads are active anymore on the stable ACTIVITY failure path - 20:33 PDT - The last decoded normal background upload in the ACTIVITY run is back at
frame_0610 - 20:43 PDT - One-shot post-init framebuffer clear removes the stale
04900title, but not the settled04910+ocean - 20:46 PDT - The settled
04910+ACTIVITY ocean comes directly from the exactOCEAN0?.SCRbootstrap load inislandInit() - 20:52 PDT - Direct black-and-return at the top of
storyPlayPreparedScene(ACTIVITY.ADS, 1)still does not move the settled04910+ocean - 20:56 PDT - Even returning from
storyPlay()immediately after an exact no-launch still does not move the settled04910+ocean - 21:01 PDT - If exact ACTIVITY still has
numThreads == 0immediately after the firstadsPlayChunk(), the whole late boundary turns black - 21:06 PDT - The first exact ACTIVITY chunk definitely reaches authored startup
ADD_SCENE(1,12)/ADD_SCENE(1,13) - 19:39 PDT - Late ACTIVITY background-thread suppression does not move the settled ocean
- 19:41 PDT - Late
ttmPlay()suppression also does not move the settled ocean - 19:43 PDT - Zeroing all background tiles immediately before
LoadImage()still does not move the settled ocean - 19:47 PDT - Direct late framebuffer black upload does change the settled ACTIVITY image
- 19:51 PDT - Replacing the
grDrawBackground()upload source with black does change the settled ACTIVITY image - 19:56 PDT - Overwriting the exact heap upload slice still does not move the settled ocean
- 20:00 PDT - Fresh-address upload only perturbs the lead-in, not the settled
04910ocean - 20:01 PDT - Widened fresh-address upload window is cleanly negative
- 20:05 PDT - Forcing full-height uploads across
04900–04910makes the settled ocean disappear - 20:10 PDT - Forcing full-height uploads with the normal source does nothing
- 18:55 PDT - Returning from
islandInit()immediately aftergrLoadScreen(...)reproduces the exact original ocean hash - 19:00 PDT - Scrubbing to black immediately after exact
adsPlay(ACTIVITY.ADS, 1)no-launch return does nothing - 19:05 PDT - Scrubbing to black immediately before
storyPlayPreparedScene(finalScene, ...)also does nothing - 19:10 PDT - Forcing the obvious
bootScene == NULLfallback prepare path to black also does nothing - 19:16 PDT - Even
adsReleaseIsland(); adsNoIsland();before exact playback leaves the original ocean hash untouched - 19:21 PDT - Forcing late
grLoadScreen(\"OCEAN0?.SCR\")loads to black also does nothing - 19:26 PDT - Zeroing restored background tiles every frame still leaves the exact original ocean hash
- 19:30 PDT - Disabling saved-rect replay still leaves the exact original ocean hash
- 18:55 PDT - Returning from
islandInit()immediately aftergrLoadScreen(...)reproduces the exact original ocean hash - 19:00 PDT - Scrubbing to black immediately after exact
adsPlay(ACTIVITY.ADS, 1)no-launch return does nothing - 19:05 PDT - Scrubbing to black immediately before
storyPlayPreparedScene(finalScene, ...)also does nothing - 19:10 PDT - Forcing the obvious
bootScene == NULLfallback prepare path to black also does nothing - 19:16 PDT - Even
adsReleaseIsland(); adsNoIsland();before exact playback leaves the original ocean hash untouched - 19:21 PDT - Forcing late
grLoadScreen(\"OCEAN0?.SCR\")loads to black also does nothing - 19:26 PDT - Zeroing restored background tiles every frame still leaves the exact original ocean hash
- 18:55 PDT - Returning from
islandInit()immediately aftergrLoadScreen(...)reproduces the exact original ocean hash - 19:00 PDT - Scrubbing to black immediately after exact
adsPlay(ACTIVITY.ADS, 1)no-launch return does nothing - 19:05 PDT - Scrubbing to black immediately before
storyPlayPreparedScene(finalScene, ...)also does nothing - 19:10 PDT - Forcing the obvious
bootScene == NULLfallback prepare path to black also does nothing - 19:16 PDT - Even
adsReleaseIsland(); adsNoIsland();before exact playback leaves the original ocean hash untouched - 19:21 PDT - Forcing late
grLoadScreen(\"OCEAN0?.SCR\")loads to black also does nothing - 18:55 PDT - Returning from
islandInit()immediately aftergrLoadScreen(...)reproduces the exact original ocean hash - 19:00 PDT - Scrubbing to black immediately after exact
adsPlay(ACTIVITY.ADS, 1)no-launch return does nothing - 19:05 PDT - Scrubbing to black immediately before
storyPlayPreparedScene(finalScene, ...)also does nothing - 19:10 PDT - Forcing the obvious
bootScene == NULLfallback prepare path to black also does nothing - 19:16 PDT - Even
adsReleaseIsland(); adsNoIsland();before exact playback leaves the original ocean hash untouched - 18:55 PDT - Returning from
islandInit()immediately aftergrLoadScreen(...)reproduces the exact original ocean hash - 19:00 PDT - Scrubbing to black immediately after exact
adsPlay(ACTIVITY.ADS, 1)no-launch return does nothing - 19:05 PDT - Scrubbing to black immediately before
storyPlayPreparedScene(finalScene, ...)also does nothing - 19:10 PDT - Forcing the obvious
bootScene == NULLfallback prepare path to black also does nothing - 18:55 PDT - Returning from
islandInit()immediately aftergrLoadScreen(...)reproduces the exact original ocean hash - 19:00 PDT - Scrubbing to black immediately after exact
adsPlay(ACTIVITY.ADS, 1)no-launch return does nothing - 19:05 PDT - Scrubbing to black immediately before
storyPlayPreparedScene(finalScene, ...)also does nothing - 18:55 PDT - Returning from
islandInit()immediately aftergrLoadScreen(...)reproduces the exact original ocean hash - 19:00 PDT - Scrubbing to black immediately after exact
adsPlay(ACTIVITY.ADS, 1)no-launch return does nothing - 18:55 PDT - Returning from
islandInit()immediately aftergrLoadScreen(...)reproduces the exact original ocean hash - 18:45 PDT - Skipping exact scene island-state calculation perturbs the lead-in, but ocean still settles by
04910
2026-03-28 12:59 PDT
- Found and fixed a real PS1 libc corruption source in
ps1_stubs.c:vprintf()was using rawvsprintf()into a fixedchar buffer[1024]- switched it to bounded
vsnprintf(buffer, sizeof(buffer), ...)
- Validation result:
- this fix was real but not sufficient by itself
- the focused
ACTIVITY.ADS 1rerun still eventually degraded intoUnknownReadHandler/UnknownWriteHandleratpc 0x8002D88C / 0x8002C998 - however, the corruption wave moved much later in wall-clock time, which proved the
vprintfoverflow was one contributor
2026-03-28 14:30 PDT
- Added later-runtime boot-state probes instead of early boot
printf/fatalErrortraps. - The compact boot bit-cell rows and the enlarged story bar panel are both too visually contaminated in live frames to trust numerically:
- at
frame_02700in the stable wrong-ocean window, the new boot-state rows still decode as zero - but the story bars saturate to junk widths like
90, so those panels are not a reliable proof surface
- at
- Switched to clean late yes/no traps in
story.c:- trap if
storyPlayPreparedScene(ACTIVITY.ADS, 1)is entered and launch fails - trap if
storyPlayPreparedScene(ACTIVITY.ADS, 1)is entered at all - trap if
storyPlay()reaches thestoryBootAdsName/storyBootAdsTagboot-scene branch forACTIVITY.ADS 1
- trap if
- Results:
- none of those traps fired in stable exact PS1 runs
- not by
2700frames:repo:/regtest-results/activity1-ads1-seed1-launchfailtrap/result.jsonrepo:/regtest-results/activity1-ads1-seed1-bootbranchtrap/result.json
- and not even by
6300frames for the prepared-scene entry trap:repo:/regtest-results/activity1-ads1-seed1-entrytrap-6300/result.json
- This is the cleanest narrowing so far:
- the stable PS1 path is not reaching
storyPlayPreparedScene(ACTIVITY.ADS, 1) - and it is not even reaching the
storyPlay()boot-scene resolution branch forstory ads ACTIVITY.ADS 1 - so the active root cause is upstream of ADS launch and upstream of story scene dispatch
- the stable PS1 path is not reaching
- Current best hypothesis:
BOOTMODE.TXTis present on disc, but thestory ads ...override is not being retained/applied by the runtime path that reachesstoryPlay()- the next target is the PS1 boot-override load/reapply path, not ACTIVITY ADS internals
- Follow-up probes on the same branch:
- Added a trap in
storyPlay()itself to dump raw boot-state (storyBootSingleSceneIndex,storyBootSceneIndex,storyBootAdsName,storyBootAdsTag) on loop entry. - The run terminated extremely early with only 3 captured frames:
repo:/regtest-results/activity1-ads1-seed1-storyloopbootstate/result.json
- But, as with earlier very-early guest traps, the expected guest fatal text still did not surface in the extracted logs.
- Added a trap in
- Practical interpretation:
- early/pre-dispatch guest fatal output remains an unreliable proof surface under the current regtest path
- later negative traps remain trustworthy:
- the stable path still does not reach
storyPlay()’s boot-scene branch forACTIVITY.ADS 1 - and still does not reach
storyPlayPreparedScene(ACTIVITY.ADS, 1)by6300
- the stable path still does not reach
- so the active bug remains upstream of story scene dispatch, in PS1 boot-override retention/application or in the transition from boot parsing into the story loop
- Then removed the remaining live PS1 scene-trace
printf()traffic from the hotACTIVITYpath while preserving the strip markers and the hard zero-IP trap:- removed
[STORY] ...boot/final prints fromrepo:/story.c - removed
[ADS] initial launch empty ...,[ADSPLAY] after chunk ..., and[ACT1LIVE] ...prints fromrepo:/ads.c
- removed
- This produced the first clean long
ACTIVITY 1PS1 run without fatal corruption:repo:/regtest-results/activity1-ads1-seed1-quiettrace6300/result.jsonframes = 6300interval = 150exit_code = 0timed_out = falseframes_captured = 41has_fatal_error = false
- Clean visual timeline from that run:
frame_02700throughframe_06000: still wrongoceanframe_06150andframe_06300: wrong liveisland/FISHING-like scene
- Clean robust-strip decode at the handoff:
repo:/regtest-results/activity1-ads1-seed1-quiettrace6300/20260328-125856/frames/jcreborn/frame_06000.pngscene_markers launched=no add_scene=no tag_hit=yes tag_miss=yes bmp_ok=no sprite_seen=yes
repo:/regtest-results/activity1-ads1-seed1-quiettrace6300/20260328-125856/frames/jcreborn/frame_06150.pngscene_markers launched=yes add_scene=yes tag_hit=no tag_miss=no bmp_ok=yes sprite_seen=no
repo:/regtest-results/activity1-ads1-seed1-quiettrace6300/20260328-125856/frames/jcreborn/frame_06300.pngscene_markers launched=yes add_scene=yes tag_hit=no tag_miss=no bmp_ok=yes sprite_seen=no
- Current read after removing the corrupting trace path:
- the earlier catastrophic memory corruption was at least partly self-induced by our PS1
printftracing - the underlying
ACTIVITY 1scene bug still remains, now exposed cleanly again - the real failure shape is:
- failed-launch / wrong-ocean window through
6000 - then wrong live
FISHING-like scene by6150
- failed-launch / wrong-ocean window through
- the earlier catastrophic memory corruption was at least partly self-induced by our PS1
- Next target:
- compare this clean
quiettrace6300PS1 run directly against the canonical hostACTIVITY-1reference window - then resume debugging the actual
6000-6150handoff with strip markers and narrow traps only, not live formatted PS1 logging
- compare this clean
2026-03-28 13:02 PDT
- Completed the clean PS1-vs-host validation on the no-printf
ACTIVITY 1run:- PS1 run:
repo:/regtest-results/activity1-ads1-seed1-quiettrace6300/result.json
- compare result:
/tmp/activity1-quiettrace6300-vs-host.json
- PS1 run:
- Compare verdict:
verdict = PIXEL_MISMATCHframe_offset = 1800common_frame_count = 3reference_coverage_ratio = 0.008426966292134831average_palette_index_diff_pixels = 126892.66666666667
- Practical meaning:
- the harness is now doing its job on a clean PS1 run
- the PS1
ACTIVITY 1sequence only brushes the host scene window at entry, then diverges almost immediately - this is a real scene/content bug, not a remaining harness artifact
- Clean visual shape remains:
frame_02700throughframe_06000: wrongoceanframe_06150andframe_06300: wrongisland/FISHING-like scene
- Current confidence statement:
- host baseline path is trusted
- sequence comparator is trusted for this scene
- PS1 logging-induced corruption has been reduced enough that the real
ACTIVITY 1failure is visible again
- Next target:
- keep
ACTIVITY 1as the first active PS1 fix - debug the actual
6000-6150handoff on the clean quiet path, without restoring the old formatted PS1 trace traffic
- keep
2026-03-28 13:31 PDT
- Ran a widened
ACTIVITY 1live-tag probe on the clean quiet path:repo:/regtest-results/activity1-ads1-seed1-livetags/result.json
- The run completed cleanly through
6300frames and kept the same visible failure shape:frame_06000: wrongoceanframe_06150,frame_06300: wrong liveislandscene
- Decoded robust-strip state at
frame_06150/frame_06300:current_ads_family_estimate = 1current_ads_tag_estimate = 1launched = trueadd_scene = truezero_ip_launch = trueactivity_live_slot2 = trueactivity_live_ip_zero = true- authored ACTIVITY live-tag one-hots remain all false for the currently instrumented tags:
activity_live_tag7 = falseactivity_live_tag8 = falseactivity_live_tag9 = falseactivity_live_tag12 = falseactivity_live_tag14 = falseactivity_live_tag2 = falseactivity_live_tag11 = falseactivity_live_tag13 = false
- Practical read:
- the wrong live scene still appears to be occurring inside
ACTIVITY.ADS tag 1, not from a clean family switch - but the active live thread visible in the robust strip still does not match any authored ACTIVITY tag we were explicitly drawing
- the wrong live scene still appears to be occurring inside
- Added a more direct overlay probe:
- raw robust rows for exact
firstIdx.sceneSlotlow bits - raw robust rows for exact
firstIdx.sceneTaglow bits - decoder support for
live_scene_slot_robust/live_scene_tag_robust
- raw robust rows for exact
- That follow-up rerun did not stay clean:
repo:/regtest-results/activity1-ads1-seed1-livetags-robust/regtest.log- it regressed into heavy
UnknownReadHandlerspam atpc 0x8002D9FCbefore producing trustworthy postprocess output
- Current state:
- harness remains good enough on the clean
quiettrace6300/livetagspath ACTIVITY 1is still the first real scene bug- the next fix/debug target is still the ACTIVITY handoff itself, but the new exact-tag probe needs to be rerun in a way that preserves the clean runtime behavior
- harness remains good enough on the clean
2026-03-28 13:40 PDT
- Reverted the invasive exact-tag overlay rows after they destabilized the runtime, and switched to one-shot engine traps instead.
- Two focused ACTIVITY probes completed cleanly:
repo:/regtest-results/activity1-ads1-seed1-zeroip-launchtrap/result.jsonrepo:/regtest-results/activity1-ads1-seed1-startup-alive-trap/result.json
- New stable behavior on both runs:
frame_06000,frame_06150, andframe_06300all remain wrongocean- the earlier wrong-island handoff is not stable enough to treat as the real bug surface
- Important negative results:
- the non-background zero-
iplaunch trap did not fire after frame5000 - the startup-thread-alive trap for
ACTIVITY slot1 tag12/tag13did not fire after frame5000
- the non-background zero-
- Static authored check:
GJDIVE.TTM tag 12reaches0x0110and then0x0FF0almost immediately, so it should not live for thousands of frames- the fact that the engine trap did not see tag
12or13still running means the older strip decode on those startup bits was over-reporting
- Current stable read:
- harness is still at practical confidence
ACTIVITY 1does not reach the intended ACTIVITY scene content on PS1- the stable bug surface is now: booted ACTIVITY scene collapses back to ocean and stays there through
6300 - the immediate next target is no longer “later ACTIVITY handoff tag”; it is “why no later successful ACTIVITY launch happens at all on the stable run”
2026-03-28 13:46 PDT
- Pulled the authored startup graph directly from generated analysis and extracted bytecode:
ACTIVITY.ADS tag 1starts withslot 1 tag 12andslot 1 tag 13- later authored graph contains
slot 1 tags 7/8/9/14/11andslot 2 tag 2
- Dumped
GJDIVE.TTM tag 12from extracted content:- it reaches
0x0110and then0x0FF0almost immediately - so it should terminate quickly, not remain the active long-lived state
- it reaches
- Added and ran a one-shot trigger trap in
adsPlayTriggeredChunks()forACTIVITY slot1 tag12/tag13:repo:/regtest-results/activity1-ads1-seed1-trigger-trap/result.json
- Result:
- trap did not fire
- stable visual output remained wrong
oceanthrough6300 - no evidence that startup
tag 12/13transitions are reaching the authored trigger path on the stable PS1 run
- Current read:
- the stable ACTIVITY bug is now below later scene handoff logic
- the next likely fault surface is the startup-thread termination / trigger pipeline itself:
- either the startup thread is not terminating in the way the ADS scheduler expects
- or the termination is happening without the expected
adsPlayTriggeredChunks(slot=1, tag=12/13)handoff
2026-03-28 13:51 PDT
- Added and ran a one-shot termination trap on the ACTIVITY startup threads:
repo:/regtest-results/activity1-ads1-seed1-terminate-trap/result.json
- Result:
- trap did not fire
- run stayed wrong
oceanthrough6300 - visual/state hash remained on the same stable bad branch as the trigger-trap run
- Combined with the previous negative trigger result:
- startup
tag 12/13is not reachingadsPlayTriggeredChunks(slot=1, tag=12/13) - and it is also not entering the normal
ADS_THREAD_TERMINATEDbranch in a way the engine sees
- startup
- Current narrowest read:
- the ACTIVITY startup branch is being lost, overwritten, or bypassed before normal termination/trigger processing
- next target should move earlier than
ADS_THREAD_TERMINATED, likely around:ttmPlay()changingisRunningnextGotoOffset/ipevolution forGJDIVE tag 12/13- or direct thread-slot reuse before the expected termination path
2026-03-28 13:55 PDT
- Added a one-shot opcode trap directly inside
ttmPlay()for ACTIVITY startup tags12/13at:0x0110(PURGE)0x0FF0(UPDATE)
- Reran the exact ACTIVITY boot:
repo:/regtest-results/activity1-ads1-seed1-ttm-opcode-trap/result.json
- Result:
- trap did not fire
- run again stayed on the same stable bad branch: wrong
oceanthrough6300
- Combined with earlier negatives:
- startup
tag 12/13does not reach normal trigger handoff - does not enter the observed
ADS_THREAD_TERMINATEDbranch - and now does not hit the expected authored early
PURGE/UPDATEpath in a way the runtime exposes
- startup
- Current best read:
- the ACTIVITY startup thread is diverging before normal TTM control flow
- next target must move even earlier than opcode-level lifetime handling:
- startup thread
ipinitialization - TTM slot/data binding
- or thread-slot reuse/corruption before the first expected
UPDATE
- startup thread
2026-03-28 13:59 PDT
- Added the earliest startup-birth trap so
ACTIVITY slot1 tag12/tag13would abort immediately afteradsAddScene()assignsip:- trap condition:
ps1AdsCurrentName == ACTIVITY.ADSps1AdsCurrentTag == 1ttmSlotNo == 1ttmTag == 12 || ttmTag == 13
- emitted fields if hit:
frameidxslottagipdataSizefirstOpcodearg3
- trap condition:
- Reran the exact scene on that build:
repo:/regtest-results/activity1-ads1-seed1-addscene-birth-trap
- Result:
- the birth trap did not fire
- no
ACT1 addScene ...fatal line appeared inprintf.log,regtest.log, or extractedtty-output.txt - the run still completed capture/postprocess on the same stable bad branch
- This is stronger than the later negative traps:
- startup
tag12/tag13is not being observed through:- normal
adsAddScene()birth on the expected ACTIVITY startup path - normal trigger handoff
- normal
ADS_THREAD_TERMINATED - normal early authored
PURGE/UPDATE
- normal
- startup
- Current best read:
- either the startup launch request is not reaching the expected
adsAddScene(slot1, tag12/13)call at all on PS1 - or
ps1AdsCurrentName/Tagis already no longer the expected ACTIVITY context by the time the launch is born
- either the startup launch request is not reaching the expected
- Next target:
- move one layer earlier than thread birth
- instrument the exact launch request state before
adsAddScene()is called:- requested ADS family/tag
- current chunk slot/tag being executed
- requested
ttmSlotNo/ttmTag - whether the ACTIVITY startup launch is being rewritten before thread creation
2026-03-28 14:03 PDT
- Added an even earlier callsite trap in
adsPlayChunk()for authored startup launches:ADD_SCENE slot1 tag12/tag13ADD_SCENE_LOCAL slot1 tag12/tag13- trap payload includes:
frame- requested
slot/tag/arg3 inRandBlockinSkipBlock- current trigger slot/tag
- Reran the exact scene:
repo:/regtest-results/activity1-ads1-seed1-addscene-callsite-trap
- Result:
- the callsite trap did not fire
- stable run still completed through
6300 - no
ACT1 add_scene_chunkorACT1 add_scene_localline appeared in logs
- This strengthens the earlier negative traps:
- on the stable PS1 path, the authored startup
ADD_SCENE slot1 tag12/tag13opcodes are not being observed at all
- on the stable PS1 path, the authored startup
2026-03-28 14:05 PDT
- Added a one-shot trap immediately after
adsLoad()insideadsPlay()for:adsResource->resName == ACTIVITY.ADSadsTag == 1- trap payload would have printed:
- selected
offset dataSizenumTags- first three opcodes at the chosen entry point
- selected
- Reran the exact scene:
repo:/regtest-results/activity1-ads1-seed1-adsload-offset-trap
- Result:
- the
adsLoad()trap did not fire - run still exited cleanly with 41 captured frames
- no
ACT1 adsLoad ...line appeared in logs
- the
- Current strongest read:
- on the stable PS1 run, we are not even entering
adsPlay("ACTIVITY.ADS", 1)on the path we thought we were - so the bug surface has moved out of ADS execution and back into the boot override / story dispatch path that is supposed to invoke
adsPlay
- on the stable PS1 run, we are not even entering
- Next target:
- instrument the story boot override path directly:
- where
story ads ACTIVITY.ADS 1is parsed - where that request is held/persisted
- and the exact site that is supposed to call
adsPlay(boot_ads_name, boot_tag)
- where
- instrument the story boot override path directly:
2026-03-28 14:11 PDT
- Probed progressively earlier boot/dispatch sites for the same exact override:
storySetBootScene("ACTIVITY.ADS", 1)storyPlay()boot-scene selection forstoryBootAdsName/storyBootAdsTagps1ApplyBootOverride()token parse forstory ads ACTIVITY.ADS 1ps1LoadBootOverride()after successful BOOTMODE readps1LoadBootOverride()early-return branches (CdSearchFile, zero-size,CdRead,CdReadSync)
- Trap strings for all of those probes are definitely present in the built executable and in the rebuilt disc snapshot:
build-ps1/jcreborn.exe- snapshot
disc/jcreborn.bin
- The rebuilt disc snapshot also definitely contains the staged override string:
story ads ACTIVITY.ADS 1 seed 1
- Yet none of those early boot traps fire at runtime, and no existing
[BOOT] ...prints appear in regtest logs either. - Practical conclusion:
- for very-early pre-graphics PS1 boot code,
printf/fatalError-style probes are no longer a trustworthy validation surface under the current regtest path - this does not invalidate the later scene harness, only the latest method of proving early-boot execution
- for very-early pre-graphics PS1 boot code,
- Next target:
- switch early boot diagnostics away from print/fatal probes and onto a non-print side effect that can be observed later in the frame output or runtime mode
- use that to prove whether
BOOTMODE.TXTis actually being loaded/applied on PS1
2026-03-28 14:48 PDT
- Confirmed a critical code-path detail in
jc_reborn.c:ps1HasBootOverridePending()intentionally ignoresstory ...overrides- so
story ads ACTIVITY.ADS 1always goes through the title path first - only
ps1ReapplyBootOverride()can preserve that request for later story dispatch
- Tried three temporary late behavioral latches keyed on:
ps1BootDbgStoryAdsAppliedps1BootDbgAdsSig == ACTIVITYps1BootDbgAdsTag == 1
- Latch attempts:
- temporary
storyPlay()reroute toBUILDING.ADS 1 - temporary
main()reroute to bench mode - temporary
main()reroute to low-levelisland ads BUILDING.ADS 1 - temporary
main()hard early exit
- temporary
- Validation runs:
repo:/regtest-results/activity1-ads1-seed1-behavioral-latch/result.jsonrepo:/regtest-results/activity1-ads1-seed1-main-latch/result.jsonrepo:/regtest-results/activity1-ads1-seed1-main-latch-ads/result.jsonrepo:/regtest-results/activity1-ads1-seed1-main-exit-latch/result.json
- Strongest result:
- the
main()hard-exit latch did not trigger - the run still followed the usual long path and dumped the normal sampled frames
- so the
story ads ACTIVITY.ADS 1request is not surviving far enough to trip a post-reapply latch inmain()
- the
- Caveat:
- 2700-frame visual controls are weak because several different boots still look the same there
- but the hard-exit latch is not visual, and its failure is the strongest proof in this set
- Current best read:
- the active bug is upstream of story dispatch and likely still inside the PS1 boot-override retention/reapply path
- specifically:
BOOTMODE.TXTmay be read, but the parsedstory ads ...request is not surviving into the later runtime state we expected
- Next target:
- inspect where story boot globals are cleared between
ps1ApplyBootOverride()and the laterstoryPlay()path - focus on
storyResetBootState()/storySetBoot*()callsites and any startup path that may be wiping story override state after reapply
- inspect where story boot globals are cleared between
2026-03-28 15:03 PDT
- Replaced the old RAM-only post-title reapply with a second
ps1LoadBootOverride()afterloadTitleScreenEarly()inrepo:/jc_reborn.c. - Validation:
- old long failing probe:
repo:/regtest-results/activity1-ads1-seed1-quiettrace6300/result.json
- new long probe with reload-after-title:
repo:/regtest-results/activity1-ads1-seed1-reload-after-title-6300/result.json
- old long failing probe:
- Important result:
- state hashes changed
- but sampled PNGs at
2700,6000,6150, and6300remained pixel-identical to the old failing run
-
So the reload changes internal state retention, but it is not yet a visible scene fix.
- Then reran hard latches on the reload-after-title path:
- top-of-
storyPlay()boot-vars latch:repo:/regtest-results/activity1-ads1-seed1-story-loop-latch/result.json- fired
storyBootAdsName/storyBootAdsTagads branch latch:repo:/regtest-results/activity1-ads1-seed1-boot-branch-latch/result.json- fired
storyBootSingleSceneIndexhijack latch:repo:/regtest-results/activity1-ads1-seed1-single-index-latch/result.json- did not fire
storyBootSceneIndexhijack latch:repo:/regtest-results/activity1-ads1-seed1-scene-index-latch/result.json- did not fire
- non-null
bootScene = storyFindSceneByAds(...)latch:repo:/regtest-results/activity1-ads1-seed1-bootscene-null-check/result.json- fired
- later post-selection / prepared-scene latches:
repo:/regtest-results/activity1-ads1-seed1-prepared-exit-latch/result.jsonrepo:/regtest-results/activity1-ads1-seed1-postselect-latch/result.json- did not fire
- top-of-
- Current narrow read:
- with reload-after-title, the
story ads ACTIVITY.ADS 1override now survives intostoryPlay() - it reaches the ads boot branch
- it resolves a non-null
bootScene - it is not being hijacked by
storyBootSingleSceneIndexorstoryBootSceneIndex - but the path still does not survive to the later post-selection / prepared-scene launch boundary
- with reload-after-title, the
- Next target:
- inspect the exact control flow between a non-null
bootSceneand the later post-selection / prepared-scene path - this is now the narrowest unexplained edge for
ACTIVITY 1
- inspect the exact control flow between a non-null
2026-03-28 15:13 PDT
- Probed deeper around the exact
bootScene -> finalScene -> storyPrepareSceneState()handoff. - Results:
finalScene = bootSceneimmediate latch:repo:/regtest-results/activity1-ads1-seed1-finalscene-assign-latch/result.json- fired
- top of
storyPrepareSceneState(ACTIVITY.ADS, 1):repo:/regtest-results/activity1-ads1-seed1-preparestate-entry-latch/result.json- fired
- after
storyCalculateIslandFromScene()but beforeadsInitIsland():repo:/regtest-results/activity1-ads1-seed1-pre-adsinitisland-latch/result.json- fired
- after
adsInitIsland():repo:/regtest-results/activity1-ads1-seed1-post-adsinitisland-latch/result.json- fired
- So:
ACTIVITY.ADS 1definitely survives through:- ads boot-branch lookup
finalScene = bootScenestoryPrepareSceneState(finalScene)storyCalculateIslandFromScene(finalScene)adsInitIsland()
- A same-run sticky-latch attempt that tried to carry “boot scene resolved” state forward to the later post-prepare block did not trip:
repo:/regtest-results/activity1-ads1-seed1-sticky-resolve-latch/result.json
- Current best read:
- the path definitely reaches and returns from
storyPrepareSceneState() - but the later post-prepare point is still not being observed with the same proof method
- so the remaining gap is now after island prep and before or during the later debug/state block inside
storyPlay()
- the path definitely reaches and returns from
- Next target:
- instrument the immediate span after
storyPrepareSceneState(finalScene)with a more explicit side effect than the prior return-style latch - likely by storing a sticky runtime flag after
adsInitIsland()and validating it from a later safe point or overlay decode instead of relying on local early returns
- instrument the immediate span after
2026-03-28 15:34 PDT
- The harness remains at practical full confidence for scene debugging.
-
ACTIVITY 1is still the active PS1 target, but the active bug surface has moved out of story routing and into the raft BMP load path during island prep. - Exact narrowing chain:
bootScene != NULLsurvives immediately beforestoryPrepareSceneState(finalScene):repo:/regtest-results/activity1-ads1-seed1-bootscene-nonnull-pre-prepare/result.json- fired
- the same latch after
storyPrepareSceneState(finalScene)did not fire:repo:/regtest-results/activity1-ads1-seed1-bootscene-nonnull-post-prepare/result.json- did not fire
- skipping
adsInitIsland()made the later latch fire:repo:/regtest-results/activity1-ads1-seed1-skip-adsinitisland-post-prepare/result.json- fired
- so the first corrupting call is inside
adsInitIsland()
- Then narrowed inside
adsInitIsland()/islandInit():- returning after screen-load/layer setup:
repo:/regtest-results/activity1-ads1-seed1-island-cut-after-screenload/result.json- fired
- returning after raft setup / before
BACKGRND.BMP:repo:/regtest-results/activity1-ads1-seed1-island-cut-before-backgrnd/result.json- did not fire
- returning immediately after
grLoadBmp(ttmSlot, 0, "MRAFT.BMP"):repo:/regtest-results/activity1-ads1-seed1-island-cut-after-mraft-load/result.json- did not fire
- returning after screen-load/layer setup:
-
So the first bad edge is the raft BMP load itself.
- Then narrowed inside
grLoadBmpRAM("MRAFT.BMP"):- forcing
MRAFT.BMPoff the PSB fast path did not help:repo:/regtest-results/activity1-ads1-seed1-mraft-no-psb-cut-after-load/result.json- still bad
- returning before any BMP-byte load did help:
repo:/regtest-results/activity1-ads1-seed1-mraft-cut-before-bmp-load/result.json- fired
- returning immediately after
ps1_loadBmpData()did not:repo:/regtest-results/activity1-ads1-seed1-mraft-cut-after-bmp-load/result.json- still bad
- forcing
-
So the corrupting operation is in the raw BMP data load path, before any frame-install loop.
- Final split on the BMP data loader:
- replacing
MRAFT.BMPload with a same-sized dummy zeroed heap buffer:repo:/regtest-results/activity1-ads1-seed1-mraft-dummy-buffer-post-load-cut/result.json- fired
- replacing it with
ps1_loadRawFile("\\BMP\\MRAFT.BMP;1", ...):repo:/regtest-results/activity1-ads1-seed1-mraft-rawfile-post-load-cut/result.json- still bad
- replacing
- Current best read:
- the first bad operation for stable
ACTIVITY 1is an actual PS1 CD-read path forMRAFT.BMP - it is not:
- story override retention
- scene dispatch
adsInitIsland()generallyBACKGRND.BMP- PSB decoding specifically
- frame-install into the sprite slot
- assigning a heap buffer to
bmpResource->uncompressedData
- it is specifically triggered by real CD-backed
MRAFT.BMPloading
- the first bad operation for stable
- Next target:
- inspect/fix the low-level PS1 CD-read path used by
ps1_loadBmpData()/ps1_loadRawFile() - likely around
CdReadbuffer handling / alignment / transfer semantics rather than scene logic
- inspect/fix the low-level PS1 CD-read path used by
2026-03-28 15:38 PDT
- Continued narrowing the
ACTIVITY 1failure after the raft-load boundary. -
The new question was whether the bad edge is specific to
MRAFT.BMPor to any real CD-backed read at that point in scene prep. - Additional splits:
- forcing
MRAFT.BMPoff PSB and returning before any frame-install loop:repo:/regtest-results/activity1-ads1-seed1-mraft-no-psb-preloop-cut/result.json- still bad
- returning before any BMP-byte load at all:
repo:/regtest-results/activity1-ads1-seed1-mraft-cut-before-bmp-load/result.json- fired
- returning immediately after
ps1_loadBmpData():repo:/regtest-results/activity1-ads1-seed1-mraft-cut-after-bmp-load/result.json- still bad
- forcing
-
So the first bad operation is definitely in the raw BMP data load path, before any frame metadata/surface install.
- Then split the load path itself:
- substitute a same-sized dummy zeroed heap buffer instead of any real read:
repo:/regtest-results/activity1-ads1-seed1-mraft-dummy-buffer-post-load-cut/result.json- fired
- substitute
ps1_loadRawFile("\\BMP\\MRAFT.BMP;1", ...):repo:/regtest-results/activity1-ads1-seed1-mraft-rawfile-post-load-cut/result.json- still bad
- substitute
ps1_loadRawFile("\\BMP\\BOAT.BMP;1", ...)for the raft load:repo:/regtest-results/activity1-ads1-seed1-boat-rawfile-post-load-cut/result.json- still bad
- substitute a same-sized dummy zeroed heap buffer instead of any real read:
- Current best read:
- the active bug is no longer asset-specific to
MRAFT.BMP - it is a generic real CD-read hazard at that exact scene-prep point
- specifically:
- any actual live PS1 CD-backed BMP read there corrupts later state
- a same-sized heap allocation without CD I/O does not
- so the next fix target is not scene logic or BMP parsing; it is the PS1 CD read path / DMA / buffer handling semantics used by
ps1_loadBmpData()andps1_loadRawFile()
- the active bug is no longer asset-specific to
- Next target:
- test safer alternate CD read semantics around
CdRead/CdReadSync, likely with a different buffering strategy or synchronization policy, then rerunACTIVITY 1
- test safer alternate CD read semantics around
2026-03-28 15:39 PDT
-
Kept the same stable
ACTIVITY 1proof surface and focused only on the low-level read semantics. - New discriminators:
- substitute
ps1_loadRawFile("\\BMP\\BOAT.BMP;1", ...)instead of the raft load:repo:/regtest-results/activity1-ads1-seed1-boat-rawfile-post-load-cut/result.json- still bad
- substitute a static-bounce-buffer reader that does real
CdReadinto static storage, thenmemcpyto heap:repo:/regtest-results/activity1-ads1-seed1-boat-bounce-post-load-cut/result.json- still bad
- substitute
- Current best read:
- this is not
MRAFT.BMP-specific - this is not a “DMA directly into malloc’d heap buffer” bug
- the active failure is any real live
CdReadat that scene-prep moment - so the next target is the PS1 CD state/synchronization path itself:
CdControl(CdlSetloc, ...)CdRead(...)CdReadSync(...)- possible interaction with current engine state / callbacks / timing
- this is not
- Additional check:
- inserting
cdromResetState()immediately before the live special-case read:repo:/regtest-results/activity1-ads1-seed1-boat-bounce-post-load-cut-cdreset/result.json- still bad
- inserting
- Updated read:
- this is not merely stale CD state left over from earlier boot/title work
- the next target is the live
CdRead/CdReadSyncsemantics themselves rather than reset ordering
- One more discriminator:
- switching the special-case live read to the legacy
ps1_fopen("BMP\\BOAT.BMP", "rb")/ps1_fread(...)whole-file path:repo:/regtest-results/activity1-ads1-seed1-boat-ps1fopen-post-load-cut/result.json- still bad
- switching the special-case live read to the legacy
- Current best read:
- it is not a bug isolated to
ps1_streamRead() - it is not a bug isolated to
ps1_loadRawFile() - it is not a bug isolated to pack reads vs fallback reads
- it is not a bug isolated to DMA directly into heap
- it is any live CD-backed read path at that scene-prep moment
- it is not a bug isolated to
- Next target:
- stop trying alternate live readers
- shift to an architectural fix:
- prefetch/cache needed BMP bytes before entering the sensitive scene-prep window, or
- otherwise guarantee
ACTIVITY 1island prep does not issue live CD reads there
2026-03-28 16:08 PDT
- Tried the architectural fix in two steps:
- added island-support BMP priming (
MRAFT.BMP,BACKGRND.BMP,HOLIDAY.BMP) toadsPrimeRestorePilotResources()for any restore pilot that usesISLETEMP.SCR - then moved priming earlier into
storyPrepareSceneState()and prearmed the pilot pack before priming
- added island-support BMP priming (
- Validation runs:
repo:/regtest-results/activity1-ads1-seed1-prefetch-6300/20260328-155810/frames/jcreborn/frame_06150.pngrepo:/regtest-results/activity1-ads1-seed1-prepprime-6300/20260328-160137/frames/jcreborn/frame_06150.pngrepo:/regtest-results/activity1-ads1-seed1-prearm-prime-6300/20260328-160314/frames/jcreborn/frame_06150.png
- Result:
- none of those three ACTIVITY 1 runs changed the late failure band
- frames
6000,6150, and6300stayed on the same wrong ocean output - so the naive “prime earlier” hypothesis is not sufficient by itself
- Follow-up probe:
- added a temporary
islandInit()residency trap aroundMRAFT.BMP - short proof run:
repo:/regtest-results/activity1-ads1-seed1-mraft-fatalproof/result.json
- this did not trip the late fatal by
3000frames - sampled visuals on that run were title/black rather than the old ocean proof surface
- added a temporary
- Current best read:
- the harness remains good enough for scene debugging
ACTIVITY 1is still the active scene- the early-prime fix did not solve the issue
- the next unresolved edge is no longer just “load MRAFT earlier”; it is the exact scene/boot context that exists when island prep is attempted
- Immediate next target:
- replace the temporary
islandInit()residency trap with a cleaner proof of the active ADS/tag/context at island-prep time - then determine whether the wrong-ocean path is entering island prep under the wrong scene identity, or whether the resource residency is still being lost before use
- replace the temporary
2026-03-28 16:18 PDT
- Replaced the failed overlay/decoder angle with a stronger runtime proof:
- temporarily forced a distinctive
ACTIVITY 1-only island state instoryPrepareSceneState():night = 1holiday = 3
- temporarily forced a distinctive
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-nightproof-6300/20260328-161502/frames/jcreborn/frame_06150.png
- Important result:
- the old wrong bright-cyan day ocean at
6000/6150/6300changed into a night-ocean variant - so the visible bad path is definitely carrying the prepared
ACTIVITY 1island state forward - this rules out the hypothesis that the wrong-ocean branch is some unrelated post-title scene path ignoring
storyPrepareSceneState(finalScene)
- the old wrong bright-cyan day ocean at
- Current best read:
ACTIVITY 1background/island state prep is being applied- but the actual scene content / sprite-thread side is still not launching or composing correctly
- so the remaining bug is now narrower again:
- not story boot routing
- not final-scene selection
- not island-state preparation
- still in the transition from prepared scene state into live ADS/TTM scene content
- Cleanup:
- reverted the temporary night/holiday proof patch after recording the result
- Next target:
- return to the clean
ACTIVITY 1path and debug why prepared ACTIVITY background state survives while the ACTIVITY scene threads/content still collapse into empty ocean
- return to the clean
2026-03-28 16:21 PDT
- Removed the remaining intrusive ACTIVITY-specific fatal traps from
ads.cso the runtime proof surface is passive again:- zero-IP launch abort
- trigger abort
- startup-thread alive/terminated aborts
- slot-2 zero-IP handoff aborts
- Clean validation run:
repo:/regtest-results/activity1-ads1-seed1-cleantelemetry-6300/20260328-161912/frames/jcreborn/frame_06150.png
- Result:
- removing those ACTIVITY-specific aborts did not change the visible scene behavior
- frame
6150is back to the same bright day-ocean failure shape as before - so the remaining bug is not an artifact of the old ACT1 fatal instrumentation
- Current best read:
- harness is still at practical full confidence
- the prepared-scene state can be proven to survive
- the cleaned runtime still collapses to empty ocean instead of real ACTIVITY scene content
- next target remains the live ADS/TTM content path rather than story boot or island-state prep
2026-03-28 16:30 PDT
- Added two temporary late proofs for the clean
ACTIVITY 1path:- in
story.c, afterstoryPlayPreparedScene(finalScene, ...), fatal ifACTIVITY.ADS 1returns withps1AdsLastPlayLaunched == 0 - in
ads.c, inside theadsPlay()main loop, fatal ifACTIVITY.ADS 1is still running past frame5400withps1AdsLastPlayLaunched == 0
- in
- Validation runs:
repo:/regtest-results/activity1-ads1-seed1-launchproof-6300/result.jsonrepo:/regtest-results/activity1-ads1-seed1-adsloop-launchtrap/result.json
- Result:
- neither proof fired
- both runs still landed on the same stable late-ocean failure band:
frame_05400throughframe_06300
scene_markers_last.launchedremainedfalsein both runs
- Important narrowing:
- the visible bad path is not simply:
storyPlayPreparedScene()returning immediately with no launch, oradsPlay()staying live into the ocean band with no launch
- so the remaining false-
launchedstate is being reached through a different path than those two proof sites capture
- the visible bad path is not simply:
- Cleanup:
- reverted both temporary proof guards after recording the negative result
- Next target:
- move the proof surface away from those two sites and identify where the late ocean path is actually re-entering/retrying scene control while leaving
ps1AdsLastPlayLaunchedfalse
- move the proof surface away from those two sites and identify where the late ocean path is actually re-entering/retrying scene control while leaving
2026-03-28 16:35 PDT
- Tested the most obvious remaining re-entry hypothesis directly in
storyPlay():- temporary proof patch on the
if (!ps1AdsLastPlayLaunched)retry branch - for
ACTIVITY.ADS 1, force a visible island-state mutation there:night = 1holiday = 3
- temporary proof patch on the
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-retrybranch-nightproof/result.json
- Result:
- the late
5400–6300ocean frames did not change at all - same day-ocean output
- same
state_hash - same false
launchedmarker
- the late
- Important narrowing:
- the stable late-ocean band is not coming from the obvious
storyPlay()failed-launch retry branch either - combined with the earlier negative proofs, the bad path is bypassing:
- the post-
storyPlayPreparedScene()failed-launch branch - the late
adsPlay()in-loop no-launch proof - the obvious
storyPlay()failed-launch retry branch side effect
- the post-
- the stable late-ocean band is not coming from the obvious
- Cleanup:
- reverted the temporary retry-branch proof patch after recording the result
- Next target:
- move one layer outside of
storyPlay()control-flow assumptions and identify what code path is drawing the late ocean band while all three expected no-launch branches fail to explain it
- move one layer outside of
2026-03-28 16:40 PDT
- Tested whether the late ACTIVITY ocean band was being sustained by the persistent island background thread itself:
- temporary proof patch in
storyPrepareSceneState() - for
ACTIVITY.ADS 1, calladsReleaseIsland()immediately afteradsInitIsland()
- temporary proof patch in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-noislandbg-proof/result.json
- Result:
- no change at all
- same late ocean frames
- same
state_hash - same false
launchedmarker
- Important narrowing:
- the late bad band is not being sustained by the background/wave thread
- it is a static ocean background already baked into
grBackgroundSfcduring island prep - the real missing piece is still the ACTIVITY scene content layer, not the island background thread lifetime
- Cleanup:
- reverted the temporary
adsReleaseIsland()proof patch after recording the result
- reverted the temporary
- Next target:
- focus directly on why ACTIVITY scene content never composites over that already-prepared ocean background
2026-03-28 16:45 PDT
- Proved the late ACTIVITY ocean band is not receiving live sprite draws at all:
- temporary fatal traps added to:
grDrawSprite()grDrawSpriteFlip()
- only for
ACTIVITY.ADS 1after frame5400
- temporary fatal traps added to:
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-latedraw-trap/result.json
- Result:
- neither draw trap fired
- the run still reached the same stable late-ocean band unchanged
- so by
5400–6300, no ACTIVITY scene sprite draw is reaching the renderer at all
- Important narrowing:
- the missing ACTIVITY content layer is failing before
grDrawSprite()/grDrawSpriteFlip() - the ocean band is just the prepared static background with no live content ops arriving in the late window
- the missing ACTIVITY content layer is failing before
- Cleanup:
- reverted the temporary late draw traps after recording the result
- Next target:
- move one layer earlier than rendering:
- determine why
ttmPlay()for the supposed ACTIVITY content is no longer issuingDRAW_SPRITEopcodes by the late window
- determine why
- move one layer earlier than rendering:
2026-03-28 16:50 PDT
- Moved one layer earlier than rendering and proved no ACTIVITY opcode execution reaches the late window either:
- temporary fatal trap at the entry of
ttmPlay() - only for
ACTIVITY.ADS 1after frame5400
- temporary fatal trap at the entry of
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-latettmp-trap/result.json
- Result:
- the
ttmPlay()trap did not fire - the run still reached the same stable late-ocean band unchanged
- the
- Important narrowing:
- by
5400–6300, no ACTIVITY thread is even reaching opcode execution anymore - combined with the earlier late draw proof, the ACTIVITY content layer is dead before:
ttmPlay()grDrawSprite()grDrawSpriteFlip()
- by
- Cleanup:
- reverted the temporary
ttmPlay()trap after recording the result
- reverted the temporary
- Next target:
- move earlier again to the thread lifecycle itself:
- why the ACTIVITY scene threads have vanished before the late window while the prepared static ocean background remains on screen
- move earlier again to the thread lifecycle itself:
2026-03-28 16:59 PDT
- Added a sticky
ps1StoryDbgBootReturnSeenproof bit for theif (bootScene != NULL) { ... return; }success-return branch instoryPlay(). - First attempted to expose that bit in the top-left drop panel, but that surface was contaminated by unrelated lit pixels earlier in the run and was not trustworthy.
-
Moved the same proof bit into the compact scene-identity strip and decoded it directly on the late ACTIVITY frame.
- Validation runs:
repo:/regtest-results/activity1-ads1-seed1-bootreturn-proof/result.jsonrepo:/regtest-results/activity1-ads1-seed1-bootreturn-compactproof/result.json
- Clean result:
- at
frame_06150,boot_return_seen_estimate = 0 - so the late ocean band is not post-return residue from the
bootScene != NULLsuccess path
- at
- Important narrowing:
- the stable late bad band is still being reached before the boot-scene success-return branch
- that rules out one of the last obvious “scene already ended and left its background behind” explanations
2026-03-28 17:02 PDT
- Added sticky ACTIVITY attempt counters:
ps1StoryDbgActivityPrepareCountps1StoryDbgActivityPlayPreparedCount
-
The compact-strip decode for those counters was too noisy to trust numerically, so I switched back to a behavioral proof.
- Behavioral proof:
- only on the second
storyPrepareSceneState(ACTIVITY.ADS, 1)call, forcedislandState.night = 1andholiday = 3
- only on the second
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-second-prepare-nightproof/result.json
- Result:
- the late
5400–6300ocean stayed the same bright day ocean - no night mutation appeared
- the late
- Important narrowing:
- the stable bad path is not repeatedly re-preparing
ACTIVITY 1before the late window - current best read is now:
- a single ACTIVITY scene-prep/background-prep pass survives
- live ACTIVITY content still dies before late
ttmPlay()and late draw - and the engine has not yet taken the normal boot-scene success-return path by the late sampled window
- the stable bad path is not repeatedly re-preparing
- Cleanup:
- reverted the temporary second-prepare nightproof after recording the result
- Next target:
- prove whether
storyPlayPreparedScene(finalScene, ...)is being entered on the stable ACTIVITY path in the current clean runtime, using a similarly late-safe behavioral proof rather than a noisy counter decode
- prove whether
2026-03-28 17:06 PDT
- Tried a simpler ACTIVITY entry proof by exposing sticky booleans for:
storyPrepareSceneState(ACTIVITY.ADS, 1)seenstoryPlayPreparedScene(ACTIVITY.ADS, 1)seen
-
The compact-strip decode for those added rows still overlapped other overlay content and was not reliable enough to trust numerically.
- Switched back to the behavior surface that already proved reliable:
- temporary mutation at the top of
storyPlayPreparedScene(ACTIVITY.ADS, 1):- force
night/holiday - rebuild island via
adsReleaseIsland(); adsInitIsland();
- force
- temporary mutation at the top of
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-playprepared-nightproof/result.json
- Result:
- late
5400–6300output stayed the same bright day ocean - no night mutation appeared
- late
- Important narrowing:
- on the stable clean ACTIVITY failure path, the renderer-visible late ocean is not coming through
storyPlayPreparedScene(finalScene, ...) - combined with the earlier proofs:
- it is not a repeated second prepare
- it is not the boot-scene success-return residue
- it is not late
ttmPlay()/ late draw activity
- on the stable clean ACTIVITY failure path, the renderer-visible late ocean is not coming through
- Current best read:
- the bad ACTIVITY path is diverging before the normal prepared-scene playback function
- but after enough ACTIVITY-specific scene preparation/background work has already happened to leave the static ocean in place
- Cleanup:
- reverted the temporary
storyPlayPreparedScene()nightproof after recording the result
- reverted the temporary
- Next target:
- identify the pre-
storyPlayPreparedScene()path that can leave ACTIVITY-prepared ocean background on screen while never entering the normal prepared-scene playback path
- identify the pre-
2026-03-28 17:09 PDT
- Tested whether the stable bad ACTIVITY path is still using the resolved
bootScene != NULL/finalScenepreparation path at all. - Temporary behavioral proof:
- immediately after
storyPrepareSceneState(finalScene), only when:bootScene != NULLfinalScene == ACTIVITY.ADS tag 1finalSceneis an island scene
- force
night/holiday - rebuild island via
adsReleaseIsland(); adsInitIsland();
- immediately after
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-bootscene-nightproof/result.json
- Result:
- late
5400–6300output stayed the same bright day ocean - no night mutation appeared
- late
- Important narrowing:
- the stable bad ACTIVITY path is not the normal resolved
bootScene/finalScenepreparation path either - combined with earlier proofs, the visible late ocean is now outside all of these expected sites:
- boot-scene success return
- repeated second prepare
- normal
storyPlayPreparedScene(finalScene, ...) - normal resolved
bootScene/finalSceneisland-prep mutation point
- the stable bad ACTIVITY path is not the normal resolved
- Cleanup:
- reverted the temporary
bootScene/finalScenenightproof after recording the result
- reverted the temporary
- Current best read:
- the late ACTIVITY bad band is being reached through an earlier or parallel path that still leaves an ACTIVITY-like prepared ocean background in place, but bypasses the normal boot-scene/final-scene playback and return surfaces we have tested
- Next target:
- step out of
story.cproofs and instrument who is writing the late ocean background surface (grBackgroundSfc) in the ACTIVITY failure path, since the control-flow proofs in the normal scene path are now mostly exhausted
- step out of
2026-03-28 17:15 PDT
- Switched proof surface from
story.ccontrol flow to the actual background writer inisland.c. - Added a helper in
story.c/repo:/story.h:storyBootOverrideMatches(const char *adsName, uint16 adsTag)
- First writer proof:
- only under boot override
ACTIVITY.ADS 1, forceislandState.night = 1,holiday = 3at the top ofislandInit()
- only under boot override
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-islandinit-nightproof/result.json
- Result:
- the late bad band changed completely
- state hash changed from the old stable ocean hash
- Important conclusion:
- the late ACTIVITY failure surface is definitely being produced through
islandInit() - this is not just stale framebuffer residue or a path entirely outside island background setup
- the late ACTIVITY failure surface is definitely being produced through
2026-03-28 17:17 PDT
- Narrowed
islandInit()one more step:- for boot override
ACTIVITY.ADS 1, return immediately after the initialgrLoadScreen(...) - skip all later raft/cloud/island compositing in
islandInit()
- for boot override
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-islandinit-loadonly-proof/result.json
- Result:
- the late
5400–6300bad band stayed exactly the same as the original stable failure:- same state hash
- same bright day-ocean frames
- the late
- Important conclusion:
- the later raft/cloud/island compositing in
islandInit()is not required for the stable ACTIVITY failure - the initial
grLoadScreen("OCEAN0?.SCR")done byislandInit()is sufficient by itself to produce the late bad surface
- the later raft/cloud/island compositing in
- Cleanup:
- reverted the temporary early return after
grLoadScreen(...)
- reverted the temporary early return after
- Current best read:
- the stable late ACTIVITY failure is now tightly narrowed to the ocean screen load path itself
- the wrong surface is being baked in at
grLoadScreen(...), before later island composition or normal scene playback ever matters
- Next target:
- instrument why
islandInit()is reaching theOCEAN0?.SCRload on the ACTIVITY failure path, and whether the intended ACTIVITY-specific screen/background choice is ever being selected before that fallback ocean load happens
- instrument why
2026-03-28 17:20 PDT
- Added a direct ACTIVITY ADS-entry proof in
adsPlay():- while testing, if
adsPlay("ACTIVITY.ADS", 1)is entered, forcenight/holiday
- while testing, if
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-adsplay-nightproof/result.json
- Result:
- the late
5400–6300bad band did not change at all
- the late
- Important conclusion:
- the stable late ACTIVITY failure is not coming through
adsPlay("ACTIVITY.ADS", 1)entry itself
- the stable late ACTIVITY failure is not coming through
2026-03-28 17:22 PDT
- Then tested a broader story-level hypothesis:
- while boot override
ACTIVITY.ADS 1is still pending, - mutate any island
storyPrepareSceneState(scene)call tonight/holiday
- while boot override
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-any-prepare-nightproof/result.json
- Result:
- the late bad band changed completely
- Important conclusion:
- the stable late failure is still reaching some island-scene preparation while the ACTIVITY override remains pending
- but it is not necessarily the ACTIVITY scene itself
2026-03-28 17:23 PDT
- Narrowed that branch again:
- mutate only when:
bootScene == NULLstoryBootOverrideMatches("ACTIVITY.ADS", 1)finalScene->flags & ISLAND
- before
storyPrepareSceneState(finalScene)
- mutate only when:
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-bootscene-null-nightproof/result.json
- Result:
- the late bad band did not change
- Important read:
- the stable path is not explained by the simple
bootScene == NULLrandom-final-scene branch alone - but it also is not entering
adsPlay("ACTIVITY.ADS", 1)directly - and
story ads ...is confirmed to still route throughstoryPlay(), notstoryPlayBootSceneDirect()
- the stable path is not explained by the simple
- Current best read:
- the remaining control-flow suspect is now the early PS1 title/boot gating in
jc_reborn.c, especially:ps1HasBootOverridePending()- override reload/application timing around title startup
- the remaining control-flow suspect is now the early PS1 title/boot gating in
- Next target:
- test whether allowing
story adsto count as a pending boot override changes the ACTIVITY failure shape, since that is now the cleanest unresolved gating edge
- test whether allowing
2026-03-28 17:29 PDT
- Tested the early PS1 title/boot gating hypothesis directly in
repo:/jc_reborn.c:- changed
ps1HasBootOverridePending()sostory ...overrides count as pending too - this suppresses the early title preload gate for
story ads ACTIVITY.ADS 1
- changed
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-pendinggate-proof/result.json
- Result:
- this does change the early shape materially:
- earlier frames that used to show title now go black sooner
- the late ocean starts earlier (
04950instead of05400)
- but the stable late bad band is still the same ocean hash:
59cd749f...
- and host compare is still a hard mismatch:
/tmp/activity1-pendinggate-vs-host.json- verdict still
PIXEL_MISMATCH
- this does change the early shape materially:
- Comparison to old clean baseline:
/tmp/activity1-old-vs-host.json- host mismatch is unchanged in substance
- Important conclusion:
- the title/boot gate is a real factor in startup shape
- but it is not the root fix for
ACTIVITY 1 - the remaining bug still lies later, in how the pending ACTIVITY override interacts with subsequent scene preparation / scene selection after startup
- Current best read:
story adsshould probably count as a pending boot override for control-flow correctness- but even with that fix, the ACTIVITY path still falls into the same wrong late ocean state
- Next target:
- keep the pending-gate result in mind, but continue tracing the later handoff between startup and island preparation, because that is still where ACTIVITY correctness is lost
2026-03-28 17:36 PDT
- Switched from branch proofs back to family proofs inside
storyPrepareSceneState(scene)while the pending override is stillACTIVITY.ADS 1. - First tested
WALKSTUF.ADSonly:repo:/regtest-results/activity1-ads1-seed1-walkstuf-prepare-nightproof/result.json
- Then tested
FISHING.ADSonly:repo:/regtest-results/activity1-ads1-seed1-fishing-prepare-nightproof/result.json
- Result:
- neither changed the stable late ocean band
- both preserved the same ocean hash
59cd749f...
- Important conclusion:
- the earlier “any island prepare while ACTIVITY override is pending” proof is not explained by either:
WALKSTUF.ADSisland prepFISHING.ADSisland prep
- the earlier “any island prepare while ACTIVITY override is pending” proof is not explained by either:
- Current best read:
- either another island family is being prepared under the still-pending ACTIVITY override
- or the broader “any prepare” proof is interacting with shared state in a way that is not family-local
- Cleanup:
- reverted the temporary family-specific proof patch after recording both results
- Next target:
- move away from heuristic family guesses and identify the exact prepared scene family/tag through a more deterministic state surface, rather than guessing by visual classifier labels
2026-03-28 17:44 PDT
- Added exact prepared-scene debug fields in
repo:/story.c:ps1StoryDbgPreparedSceneAdsSigps1StoryDbgPreparedSceneTag
-
First tried exposing them in the legacy story counter-bar panel in
repo:/graphics_ps1.cand decoding them viarepo:/scripts/decode-ps1-bars.py. - Validation run:
repo:/regtest-results/activity1-ads1-seed1-preparedtrace/result.json
- Result:
- the legacy story panel rows saturated to full width (
90) on the late frames - so that panel is not a trustworthy decode surface for exact prepared-scene identity
- the legacy story panel rows saturated to full width (
- Follow-up:
- moved the same prepared-scene fields into the compact white-bit scene-marker strip in
repo:/graphics_ps1.c - extended
repo:/scripts/decode-ps1-bars.pyto decode:prepared_scene_ads_family_estimateprepared_scene_tag_estimate
- moved the same prepared-scene fields into the compact white-bit scene-marker strip in
- Validation rerun:
repo:/regtest-results/activity1-ads1-seed1-preparedstrip/result.json
- Late-frame decode on the compact strip:
frame_06000:prepared=0/0,requested=63/0,boot_pending=1frame_06150:prepared=0/0,requested=63/0,boot_pending=1frame_06300:prepared=0/0,requested=63/0,boot_pending=1
- Important conclusion:
- the new prepared-scene field is consistent with “no prepared scene identity survives into the late ocean band”
- but the adjacent requested-family bits are still saturating (
63) on the same compact surface - so the compact strip is not yet clean enough to treat the exact family/tag values as final truth
- Current best read:
- the ACTIVITY failure still looks like “prepared/background state without surviving scene playback”
- and the next barrier is now the proof surface itself: we need one exact scene-identity signal that is robust under late-frame scaling/filtering, not another branch guess
- Next target:
- switch from compact multi-bit family/tag encoding to a simpler, one-hot or low-cardinality exact prepared-scene proof surface that cannot saturate into ambiguous
63values under DuckStation scaling
- switch from compact multi-bit family/tag encoding to a simpler, one-hot or low-cardinality exact prepared-scene proof surface that cannot saturate into ambiguous
2026-03-28 17:51 PDT
- Replaced the ambiguous compact prepared-scene family/tag decode with one-hot prepared-family rows in the robust scene-marker strip:
prepared_activity1prepared_activityprepared_buildingprepared_fishingprepared_johnnyprepared_maryprepared_miscgagprepared_standprepared_suzyprepared_visitorprepared_walkstuf
- Validation rerun:
repo:/regtest-results/activity1-ads1-seed1-preparedfamily/result.json
- Late-frame decode:
frame_06000: all prepared-family one-hots0,boot_pending=1frame_06150: all prepared-family one-hots0,boot_pending=1frame_06300: all prepared-family one-hots0,boot_pending=1
- Whole sampled run scan:
- the one-hot family rows only light during the early noisy startup frames (
00300,00450,00600), where they all saturate simultaneously and are not trustworthy - after that, no prepared-family row lights again
boot_pendingremains asserted from04950through06300
- the one-hot family rows only light during the early noisy startup frames (
- Important conclusion:
- on the stable ACTIVITY failure path, there is no trustworthy evidence that
storyPrepareSceneState()reaches any concrete island family during the late bad window - the bug target moves earlier again:
- the override remains pending
- but stable scene preparation is not being reached on the path that leads to the late ocean band
- on the stable ACTIVITY failure path, there is no trustworthy evidence that
- Current best read:
- the late ocean band is still real and stable
- but it is no longer defensible to describe it as “late prepared scene X”
- instead, the stronger statement is:
boot_pendingsurvives- normal prepared-scene identity does not
- Next target:
- stop trying to identify a late prepared family
- instrument the handoff that should consume the pending override and enter
storyPrepareSceneState()on the stable path, because that handoff is now the narrowest unexplained edge
2026-03-28 17:57 PDT
- Added sticky handoff markers for the
ACTIVITY.ADS 1pending-override path inrepo:/story.c:pending_boot_resolvedpending_preparepending_play_preparedpending_play_prepared_returned
-
Exposed those as robust white-block rows in
repo:/graphics_ps1.cand decoded them inrepo:/scripts/decode-ps1-bars.py. - Validation rerun:
repo:/regtest-results/activity1-ads1-seed1-pendinghandoff/result.json
- Late-frame decode:
frame_06000: all handoff markers0,boot_pending=1frame_06150: all handoff markers0,boot_pending=1frame_06300: all handoff markers0,boot_pending=1
- Whole sampled run scan:
- all four sticky handoff markers light only during the early noisy startup frames (
00300,00450,00600) - after that, none of them light again
boot_pendingremains asserted from04950through06300
- all four sticky handoff markers light only during the early noisy startup frames (
- Important conclusion:
- on the stable ACTIVITY failure path, there is still no trustworthy evidence that the pending override reaches:
- resolved boot-scene handoff
- stable prepare entry
- stable play-prepared entry
- stable play-prepared return
- the only late surviving signal is still the pending override itself
- on the stable ACTIVITY failure path, there is still no trustworthy evidence that the pending override reaches:
- Current best read:
- the stable failure path is earlier than the normal scene handoff/control-flow surfaces we have been instrumenting
- the early startup/title window is still polluting these markers, but the late window is now consistent:
- pending override survives
- no later stable handoff marker survives with it
- Next target:
- stop relying on markers that can be polluted by the early startup samples
- instrument a later-safe state transition that only becomes writable after intro/title flow is complete, so we can distinguish “never consumed” from “consumed then reset”
2026-03-28 18:01 PDT
- Added a late-safe gate to the sticky pending-handoff markers in
repo:/story.c:- the ACTIVITY handoff latches now only set after
grGetCurrentFrame() >= 1000
- the ACTIVITY handoff latches now only set after
- Validation rerun:
repo:/regtest-results/activity1-ads1-seed1-pendinghandoff-late/result.json
- Result:
- the sampled late frames still show only:
boot_pending=1
- all of these remain
0at04950through06300:pending_boot_resolvedpending_preparepending_play_preparedpending_play_prepared_returned
- the sampled late frames still show only:
- Important conclusion:
- gating those markers behind a later frame threshold did not change the core read
- the stable ACTIVITY failure path still preserves only the pending override
- it does not preserve any trustworthy evidence of the later normal handoff surfaces
- Current best read:
- the unexplained edge is now even narrower:
- the override remains pending after startup
- but the code path that should consume it into normal scene handoff still leaves no late stable trace
- the unexplained edge is now even narrower:
- Next target:
- move one layer lower than the current story markers and find a later-safe “override consumed” state that cannot be confused with startup/title replay
2026-03-28 18:06 PDT
- Split the late pending-override source further in
repo:/story.c:late_boot_ads_exactlate_boot_lookup_nulllate_boot_lookup_found
-
Exposed those as late sticky rows in
repo:/graphics_ps1.cand decoded them inrepo:/scripts/decode-ps1-bars.py. - Validation rerun:
repo:/regtest-results/activity1-ads1-seed1-latebootsource/result.json
- Result:
- after startup, from
04950through06300, the only surviving late signal is still:boot_pending=1
- all of these remain
0in the late window:late_boot_ads_exactlate_boot_lookup_nulllate_boot_lookup_foundpending_boot_resolved
- after startup, from
- Important conclusion:
- the new split markers still only light in the polluted early startup samples and do not survive into the late ACTIVITY failure path
- so they do not change the late-path read:
- stable late evidence still shows only an unresolved pending override
- Current best read:
- the late ocean band is downstream of “override still pending”
- but upstream of every stable story-level resolution/prepare/play marker we have made durable so far
- Next target:
- stop extending the same story overlay family of markers
- move to a different proof surface entirely for the next cut, because the current late-safe story markers are no longer adding new separation
2026-03-28 18:11 PDT
- Switched from passive markers to a behavioral proof in
repo:/story.c:- if the exact
ACTIVITY.ADS 1override was still pending after frame1000, clear it immediately viastoryClearBootSceneOverrideOnly()
- if the exact
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-lateclear/result.json
- Result:
- the stable late output did not change at all
- the late frame hash is byte-identical to the previous late-handoff run:
- old
frame_06150:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc - new
frame_06150:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- old
- Important conclusion:
- the lingering ACTIVITY boot override is not the active cause of the late ocean band
- it is residue
- the real failure has already happened earlier by the time the late window is reached
- Cleanup:
- reverted the temporary late-clear proof patch after validating the identical output
- Current best read:
- the active bug is now clearly upstream of the surviving
boot_pendingresidue - continuing to chase the late pending-override state itself will not produce the fix
- the active bug is now clearly upstream of the surviving
- Next target:
- shift the proof surface from late residual story state to the earlier point where the ACTIVITY path first diverges into the wrong ocean outcome
2026-03-28 18:15 PDT
- Ran a finer-grained ACTIVITY boundary scan with 30-frame sampling:
repo:/regtest-results/activity1-ads1-seed1-boundary5100/result.json
- Result:
- on the current build, the stable wrong-ocean hash first appears at:
frame_04920
- before that, the run is still changing through several distinct earlier states
- on the current build, the stable wrong-ocean hash first appears at:
- Important conclusion:
- the useful instrumentation window is now much tighter
- there is no need to keep probing the broad
5000+late band
- Current best read:
- the ACTIVITY failure becomes stable at
04920 - so the next debug cut should target the transition band immediately before that:
- roughly
04800–04920
- roughly
- the ACTIVITY failure becomes stable at
- Next target:
- instrument the first bad transition window directly around
04920, rather than chasing residual state after the failure has already stabilized
- instrument the first bad transition window directly around
2026-03-28 18:28 PDT
- Tightened the first stable failure onset again with a 10-frame boundary scan:
repo:/regtest-results/activity1-ads1-seed1-boundary5000i10/result.json
- Result:
- first stable bad frame is now pinned to:
frame_04910
- last frame before it:
frame_04900
- first stable bad frame is now pinned to:
- Useful observation:
frame_04900 -> frame_04910is the real settled flipframe_04910 -> frame_04920is byte-identical- so
04910is the first stable wrong-ocean frame, not just part of a slow drift
- Behavioral proof:
- temporarily bypassed
adsPlayIntro()inrepo:/story.conly for exactACTIVITY.ADS 1 - validation run:
repo:/regtest-results/activity1-ads1-seed1-nointro/result.json
- temporarily bypassed
- Result:
- the stable late bad ocean disappears entirely
- replaced by a stable black-screen path
- final state hash changes from the ocean hash to:
df57118101f4e25e7e8a74ed77aaadb00f9ed9ea28e1cff7236c070d1ffaed5e
- visual detect at
frame_04910andframe_05000:black/ no island / no content
- Important conclusion:
- the settled bad ocean is directly sourced by the normal
adsPlayIntro()boot path - bypassing intro does not fix ACTIVITY, but it proves the late ocean surface is not some unrelated later fallback
- it comes from the intro/ocean boot sequence that the story path normally uses before ACTIVITY should take over
- the settled bad ocean is directly sourced by the normal
- Cleanup:
- reverted the temporary
adsPlayIntro()bypass after validating the black-screen replacement path
- reverted the temporary
- Current best read:
- the active bug is now strongly centered on the handoff from intro/ocean boot into the ACTIVITY scene path
- not on the residual late pending override
- Next target:
- instrument the intro-to-scene handoff itself, because that is now the most plausible place where ACTIVITY takeover fails and leaves the boot ocean in place
18:32 PDT - Post-intro scrub proof changes failure to stable title, not black or ocean
- Temporary proof:
- left normal
adsPlayIntro()inrepo:/story.c - then immediately called
adsNoIsland()only for exactACTIVITY.ADS 1
- left normal
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-postintroscrub/result.json
- Result:
- run exits cleanly with:
state_hash = d9ee0d2e8f8465ea471c9c79223f1177c7225a238274f7f68ab32ed94312c0a0
- sampled late frames remain on the title surface, not ocean:
frame_04770: titleframe_04910: titleframe_05000: title
- run exits cleanly with:
- Important comparison:
- normal path:
- stable bad ocean by
04910
- stable bad ocean by
- no-intro proof:
- stable black-screen path
- post-intro scrub proof:
- stable title path
- normal path:
- Important conclusion:
- the bad ocean is not explained only by
adsPlayIntro()being called - it depends on intro-side island state surviving after intro returns
- clearing island state immediately after intro prevents the bad ocean and instead leaves the run stranded on title
- so the ACTIVITY failure is now most strongly centered on the handoff immediately after intro returns and before ACTIVITY content successfully takes over
- the bad ocean is not explained only by
- Cleanup:
- reverted the temporary post-intro
adsNoIsland()proof patch after validation
- reverted the temporary post-intro
- Next target:
- instrument the immediate post-intro handoff, because that is now the narrowest boundary separating:
- stable title
- stable wrong ocean
- black-screen no-intro failure
- instrument the immediate post-intro handoff, because that is now the narrowest boundary separating:
18:34 PDT - Replacing first exact ACTIVITY prepare with adsNoIsland() yields black, not ocean
- Temporary proof:
- in
repo:/story.c, replaced the first exact:bootScene != NULLfinalScene == ACTIVITY.ADS 1storyPrepareSceneState(finalScene)
- with:
adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-prepare-noisland/result.json
- Result:
- run exits cleanly with:
state_hash = 0f9a705ed4b557e621649636842b051f9311c39535f4fd6159c7b3243c98a54a
- sampled late frames are black, not ocean:
frame_04770: blackframe_04910: blackframe_05000: black
- run exits cleanly with:
- Important comparison:
- normal path:
- stable bad ocean by
04910
- stable bad ocean by
- no-intro proof:
- stable black-screen path
- post-intro scrub proof:
- stable title path
- exact-prepare
adsNoIsland()proof:- stable black-screen path
- normal path:
- Important conclusion:
- the settled bad ocean is being established by the exact first
ACTIVITY.ADS 1prepare path, not merely carried forward from intro - intro alone is not enough to explain the stable ocean:
- keeping intro but scrubbing immediately after it gives title
- keeping intro but replacing the exact ACTIVITY prepare with
adsNoIsland()gives black
- so the active failure boundary is now centered tightly on what
storyPrepareSceneState(finalScene)does for exactACTIVITY.ADS 1
- the settled bad ocean is being established by the exact first
- Cleanup:
- reverted the temporary exact-prepare
adsNoIsland()proof patch after validation
- reverted the temporary exact-prepare
- Next target:
- inspect the exact
ACTIVITY.ADS 1prepare path itself:storyCalculateIslandFromScene(finalScene)adsPrimeSceneResources(...)adsInitIsland()
- because that path now has the strongest causal link to the stable wrong-ocean state
- inspect the exact
18:40 PDT - Skipping adsPrimeSceneResources() does not change the stable ocean failure
- Temporary proof:
- in
repo:/story.c, skipped:adsPrimeSceneResources(scene->adsName, scene->adsTagNo)
- only for exact
ACTIVITY.ADS 1 - left:
storyCalculateIslandFromScene(scene)adsInitIsland()- normal intro flow
- normal later playback path
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-skip-prime/result.json
- Result:
- run exits cleanly with:
state_hash = fe8a54dc926a6b88be9364720305f44a71ea7d5f511a15ab3c3462defac3996b
- sampled late frames are still stable ocean:
frame_04770: oceanframe_04910: oceanframe_05000: ocean
- run exits cleanly with:
- Important conclusion:
adsPrimeSceneResources()is not the primary cause of the settled wrong-ocean state- removing it does not collapse the run to black and does not prevent the ocean failure
- so the strongest remaining prepare-path suspects are now:
storyCalculateIslandFromScene(finalScene)adsInitIsland()- specifically
islandInit()and the initialgrLoadScreen("OCEAN0?.SCR")path it performs
- Cleanup:
- reverted the temporary skip-prime proof patch after validation
- Next target:
- split the remaining exact prepare surface between:
- island-state calculation
- and
adsInitIsland()/islandInit()
- split the remaining exact prepare surface between:
18:45 PDT - Skipping exact scene island-state calculation perturbs the lead-in, but ocean still settles by 04910
- Temporary proof:
- in
repo:/story.c, skipped:storyCalculateIslandFromScene(scene)
- only for exact
ACTIVITY.ADS 1 - left:
adsPrimeSceneResources(...)adsInitIsland()- normal intro flow
- normal later playback path
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-skip-scene-islandcalc/result.json
- Result:
- run exits cleanly with:
state_hash = 81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03
- sampled frames:
frame_04770: blackframe_04910: oceanframe_05000: ocean
- run exits cleanly with:
- Important conclusion:
- scene-specific island-state calculation does affect the lead-in before the settled failure
- but it does not prevent the stable wrong-ocean state from being re-established by
04910 - so it is not sufficient to explain the persistent ACTIVITY failure on its own
- the strongest remaining causal surface is still:
adsInitIsland()- especially
islandInit()and its initial background load/composition path
- Cleanup:
- reverted the temporary skip-scene-islandcalc proof patch after validation
- Next target:
- isolate
adsInitIsland()/islandInit()more directly, because that is now the tightest surviving source of the settled wrong-ocean outcome
- isolate
02:16 PDT - Keeping the first small pack-header buffer alive did not move ACTIVITY 1
- Temporary proof:
- in
repo:/cdrom_ps1.c, changedrepo:/cdrom_ps1.cso the first smallPS1_PACK_HEADER_SIZEallocation stayed alive until the function returned - specifically:
- kept the first
ps1_streamReadFromCdFile(&cdfile, 0, PS1_PACK_HEADER_SIZE)buffer allocated - delayed its
free(...)until after the later full header read / entry decode completed
- kept the first
- goal:
- test whether immediate reuse of that first tiny heap block was the layout-sensitive seam
- in
- Validation run:
repo:/regtest-results/activity1-packindex-keep-small-header
- Result:
- run exited cleanly with the exact old settled-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exited cleanly with the exact old settled-ocean hash:
- Important conclusion:
- keeping the first small header allocation alive is not sufficient to move the bug
- so the layout/lifetime sensitivity around
repo:/cdrom_ps1.cis still real, but it is not explained by simple reuse of that first20-byte header block alone - the stronger remaining suspects stay local to:
- stack adjacency / local object layout
CdlFILElifetime or overwrite- interaction between the two
ps1_streamReadFromCdFile(...)calls beyond just freeing the first tiny buffer
- Cleanup:
- reverted the temporary
headerDataSmalllifetime probe inrepo:/cdrom_ps1.c
- reverted the temporary
- Next target:
- inspect local-object lifetime and overwrite risk around:
cdfileheaderDataentries- the transition between the first and second
ps1_streamReadFromCdFile(...)calls
- inspect local-object lifetime and overwrite risk around:
02:18 PDT - Copying CdlFILE immediately after CdSearchFile moved ACTIVITY 1 to the third branch
- Temporary proof:
- in
repo:/cdrom_ps1.c, changedrepo:/cdrom_ps1.cso that:stableCdfile = cdfile;- both
ps1_streamReadFromCdFile(...)calls usedstableCdfile outPack->cdfilealso storedstableCdfile
- no search logic changed
- goal:
- test whether copying
CdlFILEimmediately afterCdSearchFile(...)is enough to perturb the layout-sensitive failure
- test whether copying
- in
- Validation run:
repo:/regtest-results/activity1-packindex-stable-cdfile
- Result:
- run exited cleanly with the third stable branch:
state_hash = 81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03
- late markers:
bmp_ok = truebmp_fail = truesprite_count_estimate = 0
- run exited cleanly with the third stable branch:
- Important conclusion:
- this is a strong confirmation that the live seam is no longer behaving like a pure
CdSearchFile(...)logic bug - copying
CdlFILEimmediately after search is enough to move the runtime, matching the earlier stack-layout branch - the strongest active suspects are now:
- local
CdlFILElifetime / overwrite - adjacent stack layout around
cdfile,headerData, andentries - UB or clobber between the first and second
ps1_streamReadFromCdFile(...)calls
- local
- this is a strong confirmation that the live seam is no longer behaving like a pure
- Cleanup:
- reverted the temporary
stableCdfileprobe inrepo:/cdrom_ps1.c
- reverted the temporary
- Next target:
- probe memory adjacency around
cdfilemore directly, rather than pack-search logic - likely either:
- duplicate
cdfileinto a far-separated local block - or harden / relocate other nearby locals in
repo:/cdrom_ps1.c
- duplicate
- probe memory adjacency around
02:21 PDT - Moving CdlFILE fully off the local stack also moved ACTIVITY 1
- Temporary proof:
- in
repo:/cdrom_ps1.c, changedrepo:/cdrom_ps1.cso the searched pack file record lived in a function-staticCdlFILE - specifically:
- removed local-stack
CdlFILE cdfile - used
static CdlFILE stableCdfile - passed
stableCdfileto bothps1_streamReadFromCdFile(...)calls - stored
stableCdfileintooutPack->cdfile
- removed local-stack
- in
- Validation run:
repo:/regtest-results/activity1-packindex-static-cdfile
- Result:
- run exited cleanly on the improved branch:
state_hash = 91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534d
- late markers:
bmp_ok = truebmp_fail = falsesprite_count_estimate = 63
- run exited cleanly on the improved branch:
- Important conclusion:
- this is stronger than the previous local-copy probe
- moving
CdlFILEcompletely off the hot local stack is enough to change the runtime - together, the two
CdlFILEprobes now make the most likely live seam:- local-stack adjacency around
CdlFILE cdfile - or a nearby overwrite / UB that depends on where
cdfileis laid out relative to:headerDatapackFilecdPath- later
packCdFile
- local-stack adjacency around
- Cleanup:
- reverted the temporary function-static
CdlFILEprobe inrepo:/cdrom_ps1.c
- reverted the temporary function-static
- Next target:
- probe which nearby local is interacting with
cdfilelayout - likely by moving or padding one neighboring object at a time inside
repo:/cdrom_ps1.c
- probe which nearby local is interacting with
02:24 PDT - Moving cdPath off the local stack also moved ACTIVITY 1
- Temporary proof:
- in
repo:/cdrom_ps1.c, changedrepo:/cdrom_ps1.cso the constructed pack search path lived in a function-static buffer - specifically:
- removed local-stack
char cdPath[64] - used
static char stableCdPath[64] - still kept local-stack
CdlFILE cdfile - no search logic changed beyond where the path buffer lived
- removed local-stack
- in
- Validation run:
repo:/regtest-results/activity1-packindex-static-cdpath
- Result:
- run exited cleanly on the third branch:
state_hash = 81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03
- late markers:
bmp_ok = truebmp_fail = truesprite_count_estimate = 0
- run exited cleanly on the third branch:
- Important conclusion:
- this is the strongest adjacent-local clue so far
- moving only
cdPathoff the local stack is enough to perturb the bug, even whilecdfileremains local - that makes the hottest current seam the local-stack relationship between:
char cdPath[64]CdlFILE cdfile
- current best read:
- either
CdSearchFile(...)or a nearby later operation is clobbering one of those objects - or the bug depends on exact stack adjacency between them
- either
- Cleanup:
- reverted the temporary function-static
cdPathprobe inrepo:/cdrom_ps1.c
- reverted the temporary function-static
- Next target:
- probe the
cdPath/cdfileseam directly - likely with local padding between them, or by reordering just those two locals inside
repo:/cdrom_ps1.c
- probe the
02:27 PDT - Simple local padding between cdPath and cdfile did not move ACTIVITY 1
- Temporary proof:
- in
repo:/cdrom_ps1.c, changedrepo:/cdrom_ps1.cso both:char cdPath[64]CdlFILE cdfile
- remained local-stack objects
- but a local:
volatile uint8_t cdPathPad[64]
- was inserted between them and zeroed before
CdSearchFile(...)
- in
- Validation run:
repo:/regtest-results/activity1-packindex-cdpathpad64
- Result:
- run exited cleanly on the original old-ocean branch:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exited cleanly on the original old-ocean branch:
- Important conclusion:
- the live seam is more specific than raw stack distance between
cdPathandcdfile - simply inserting padding is not enough
- the earlier moving probes likely depend on:
- exact object identity / storage class
- or exact relative ordering/layout, not just added bytes in between
- the live seam is more specific than raw stack distance between
- Cleanup:
- reverted the temporary
cdPathPadprobe inrepo:/cdrom_ps1.c
- reverted the temporary
- Next target:
- test exact local reordering rather than padding
- especially swapping or separating
cdPathandcdfileby object order insiderepo:/cdrom_ps1.c
02:31 PDT - Swapping local order of cdfile and cdPath moved ACTIVITY 1
- Temporary proof:
- in
repo:/cdrom_ps1.c, changedrepo:/cdrom_ps1.cso only the declaration order changed:- from:
char cdPath[64];CdlFILE cdfile;
- to:
CdlFILE cdfile;char cdPath[64];
- from:
- no logic changed
- no storage class changed
- in
- Validation run:
repo:/regtest-results/activity1-packindex-swap-cdfile-cdpath
- Result:
- run exited cleanly on the improved branch:
state_hash = 91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534d
- late markers:
bmp_ok = truebmp_fail = falsesprite_count_estimate = 63
- run exited cleanly on the improved branch:
- Important conclusion:
- exact local order matters here
- that is stronger than the earlier padding-negative result
- current best read:
- the live seam is a layout-sensitive overwrite / UB involving the local-stack placement of:
cdPathcdfile
- not just raw adjacency distance
- the live seam is a layout-sensitive overwrite / UB involving the local-stack placement of:
- Cleanup:
- reverted the temporary local-order swap in
repo:/cdrom_ps1.c
- reverted the temporary local-order swap in
- Next target:
- identify which operation actually clobbers or depends on that order
- likely by isolating:
CdSearchFile(&cdfile, cdPath)- the first
ps1_streamReadFromCdFile(&cdfile, ...) - and any later reuse of
cdfile
02:36 PDT - A tiny helper around CdSearchFile(...) also moved ACTIVITY 1
- Temporary proof:
- in
repo:/cdrom_ps1.c, added a tiny helper:ps1PilotSearchFile(const char *path, CdlFILE *outFile)
- and changed
repo:/cdrom_ps1.cto call that helper instead of invoking:CdSearchFile(&cdfile, cdPath)- directly
- no logic changed
- no storage class changed
- in
- Validation run:
repo:/regtest-results/activity1-packindex-search-helper
- Result:
- run exited cleanly on the improved branch:
state_hash = 91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534d
- late markers:
bmp_ok = truebmp_fail = falsesprite_count_estimate = 63
- run exited cleanly on the improved branch:
- Important conclusion:
- changing the caller frame around
CdSearchFile(...)is enough to move the runtime - that is the strongest current sign that the live seam is stack-sensitive corruption or UB at/around the
CdSearchFile(&cdfile, cdPath)call site itself - current best read:
- not a clean logical pack-search bug
- not just later pack-header parsing
- most likely a stack-sensitive overwrite interacting with:
- local
cdPath - local
cdfile - and the exact call frame for
CdSearchFile(...)
- local
- changing the caller frame around
- Cleanup:
- reverted the temporary
ps1PilotSearchFile(...)helper probe inrepo:/cdrom_ps1.c
- reverted the temporary
- Next target:
- isolate the fault surface to:
- the direct
CdSearchFile(...)call itself - versus the first subsequent
ps1_streamReadFromCdFile(...)
- the direct
- using equally small caller-frame perturbations
- isolate the fault surface to:
01:55 PDT - Evaluating the old ACTIVITY.PAK conditional alone is enough to hold the improved branch
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- kept the same conditional shape that previously correlated with the improved branch:
- literal
CdSearchFile(&verifyFile, "\\PACKS\\ACTIVITY.PAK;1") - exact expected metadata checks
- second literal
CdSearchFile(&cdfile, "\\PACKS\\ACTIVITY.PAK;1") - mismatch test against the same expected metadata
- literal
- but removed the early-return side effect
- only assigned the boolean result to a
volatile int proof_condition
- kept the same conditional shape that previously correlated with the improved branch:
- in
- Validation run:
repo:/regtest-results/activity1-activitypak-cond-noop
- Result:
- run stayed on the improved branch:
91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534d
- run stayed on the improved branch:
- Important conclusion:
- the earlier improvement did not depend on the early return being taken
- evaluating that exact conditional is enough to perturb the failure
- so the active seam now looks like a timing / register / stack-layout sensitivity around the exact
ACTIVITY.PAKlookup path, not a clean logicalCdSearchFilebranch bug
- Cleanup:
- reverted the temporary conditional-noop proof
- Next target:
- stop treating this as a pure logic bug in the pack-lookup branch
- inspect whether tiny local-layout changes around
ps1PilotLoadPackIndex(...)are masking an adjacent memory clobber orCdlFILElifetime issue
02:00 PDT - Pure local stack padding moves the run to a third stable branch
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), added:volatile uint8_t layoutPad[64];memset((void *)layoutPad, 0, sizeof(layoutPad));
- only for exact
ACTIVITY.ADS - no
CdSearchFilelogic was changed
- in
- Validation run:
repo:/regtest-results/activity1-packindex-layoutpad64
- Result:
- run moved to a third stable branch:
81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03
- markers also changed:
bmp_ok = truebmp_fail = truesprite_count_estimate = 0
- run moved to a third stable branch:
- Important conclusion:
- this is the strongest evidence so far that the live seam is a local-memory / UB / layout-sensitive bug around
ps1PilotLoadPackIndex(...) - a pure stack-layout perturbation, with no lookup-logic change, is enough to move the runtime onto a different stable failure branch
- current best read:
- adjacent local clobber
- or lifetime/overwrite involving
CdlFILE, header buffers, or nearby stack locals
- this is the strongest evidence so far that the live seam is a local-memory / UB / layout-sensitive bug around
- Cleanup:
- reverted the temporary stack-padding proof
- Next target:
- inspect exact local-object lifetime and overwrite risk inside
ps1PilotLoadPackIndex(...) - especially:
- stack-local adjacency
ps1_streamReadFromCdFile(...)consumers- and whether header/entry decode is reading/writing beyond intended bounds
- inspect exact local-object lifetime and overwrite risk inside
02:05 PDT - Basic decoded-field bounds hardening does not move the run
- Temporary hardening:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), added guards:- reject
entryBytesmultiply overflow - reject
prefetchCount > PS1_PACK_PREFETCH_MAX - reject
firstResourceOffset > cdfile.size
- reject
- left all actual lookup and decode logic otherwise unchanged
- in
- Validation run:
repo:/regtest-results/activity1-packindex-bounds-guard1
- Result:
- run still fell back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run still fell back to the old settled-ocean hash:
- Important conclusion:
- the active bug is still not explained by the most obvious corrupted-header invariants
- current evidence still points more strongly at:
- layout-sensitive UB
- local-memory clobber
- or object-lifetime misuse around
ps1PilotLoadPackIndex(...)
- Cleanup:
- reverted the temporary bounds guards
- Next target:
- probe object adjacency/lifetime more directly:
headerDataentriescdfile- and the transition between the first and second
ps1_streamReadFromCdFile(...)results
- probe object adjacency/lifetime more directly:
01:24 PDT - Root-file lookup is healthy before ACTIVITY.PAK search, but not after it
- Temporary proofs:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- before
CdSearchFile(&cdfile, cdPath), checked:CdSearchFile(&verifyFile, "\\RESOURCE.MAP;1")- expected:
- size
1461 - extent
416
- size
- and temporarily returned
0if that lookup matched exactly
- before
- separate run:
- after the normal
ACTIVITY.PAKCdSearchFile(...), checked the same\\RESOURCE.MAP;1metadata and returned0only if it still matched
- after the normal
- in
- Validation runs:
repo:/regtest-results/activity1-rootmap-presearch-proofrepo:/regtest-results/activity1-rootmap-control-proof
- Result:
- presearch proof snapped back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- postsearch proof stayed on the improved branch:
91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534d
- presearch proof snapped back to the old settled-ocean hash:
- Important conclusion:
CdSearchFileis healthy on entry tops1PilotLoadPackIndex(...)- but after the exact
ACTIVITY.PAKlookup, even a known-good root file no longer resolves with the expected metadata - so the
ACTIVITY.PAKsearch itself is poisoning laterCdSearchFilestate
- Cleanup:
- reverted the temporary
RESOURCE.MAPproofs
- reverted the temporary
- Next target:
- determine whether pack-directory lookup is also healthy before the exact
ACTIVITY.PAKsearch
- determine whether pack-directory lookup is also healthy before the exact
01:31 PDT - Another pack file resolves correctly before ACTIVITY.PAK search
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- before the normal
ACTIVITY.PAKsearch, checked:CdSearchFile(&verifyFile, "\\PACKS\\BUILDING.PAK;1")- expected:
- size
2441216 - extent
5037
- size
- and temporarily returned
0if that lookup matched exactly
- before the normal
- in
- Validation run:
repo:/regtest-results/activity1-building-presearch-proof
- Result:
- run snapped back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run snapped back to the old settled-ocean hash:
- Important conclusion:
- pack-directory lookup is also healthy on entry
- so the failure is not a general inability to resolve
PACKS\\*.PAK;1 - the live seam is now narrower: the exact
ACTIVITY.PAKlookup itself
- Cleanup:
- reverted the temporary
BUILDING.PAKpresearch proof
- reverted the temporary
- Next target:
- rule out any remaining path-string construction issue by testing a literal
\\PACKS\\ACTIVITY.PAK;1lookup
- rule out any remaining path-string construction issue by testing a literal
01:32 PDT - Literal \\PACKS\\ACTIVITY.PAK;1 lookup fails too
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- before the normal
cdPathsearch, checked a literal:CdSearchFile(&verifyFile, "\\PACKS\\ACTIVITY.PAK;1")- expected:
- size
2836480 - extent
3652
- size
- and temporarily returned
0if that literal lookup matched exactly
- before the normal
- in
- Validation run:
repo:/regtest-results/activity1-activitypak-literal-presearch-proof
- Result:
- run stayed on the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run stayed on the old settled-ocean hash:
- Important conclusion:
- this is not a
cdPathconstruction orps1PilotBuildPackFile(...)string-format issue - on current
HEAD, exactACTIVITY.PAKresolution is already wrong even when searched via the literal correct path, while:\\RESOURCE.MAP;1resolves correctly before it\\PACKS\\BUILDING.PAK;1resolves correctly before it
- the active root-cause surface is now the exact
CdSearchFile("\\PACKS\\ACTIVITY.PAK;1")lookup behavior itself
- this is not a
- Cleanup:
- reverted the temporary literal-lookup proof
- Next target:
- inspect why exact
ACTIVITY.PAKlookup is special:- possible duplicate/conflicting directory record
- path aliasing
- or pack-name-specific disc lookup behavior under DuckStation/PSn00b CD APIs
- inspect why exact
01:35 PDT - The image has exactly one ACTIVITY.PAK record, and repeated literal lookups stay on the improved branch
- Built-image inspection:
- parsed
repo:/jcreborn.bindirectly PACKSdirectory listing is clean:ACTIVITY.PAK;1extent3652, size2836480BUILDING.PAK;1extent5037, size2441216- no duplicate
ACTIVITY.PAK;1directory records
- parsed
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- performed a first literal:
CdSearchFile(&verifyFile, "\\PACKS\\ACTIVITY.PAK;1")
- required it to match:
- size
2836480 - extent
3652
- size
- then immediately performed a second identical literal:
CdSearchFile(&cdfile, "\\PACKS\\ACTIVITY.PAK;1")
- and would temporarily return
0only if that second lookup came back wrong
- performed a first literal:
- in
- Validation run:
repo:/regtest-results/activity1-activitypak-doublelookup-proof
- Result:
- run stayed on the improved branch:
91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534d
- run stayed on the improved branch:
- Important conclusion:
- the disc image itself is not the issue:
- there is a single clean
ACTIVITY.PAK;1record
- there is a single clean
- two consecutive literal
CdSearchFile("\\PACKS\\ACTIVITY.PAK;1")calls do not reproduce the old failure - so the live seam is now narrower than “ACTIVITY record is bad”
- current best read:
- the normal runtime path can be destabilized by how it reaches the later
CdSearchFile(&cdfile, cdPath)call - but a literal ACTIVITY lookup can also act as a stabilizing/priming step before the normal path continues
- the normal runtime path can be destabilized by how it reaches the later
- the disc image itself is not the issue:
- Cleanup:
- reverted the temporary repeated-literal proof
- Next target:
- test whether a single literal
ACTIVITY.PAKprelookup, with no mismatch check and no early return, is enough to stabilize the normal runtime path
- test whether a single literal
01:40 PDT - A single literal ACTIVITY.PAK prelookup is not enough
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- inserted a no-op prelookup:
CdSearchFile(&primeFile, "\\PACKS\\ACTIVITY.PAK;1")
- then continued into the normal:
CdSearchFile(&cdfile, cdPath)
- no early return, no mismatch branch
- inserted a no-op prelookup:
- in
- Validation run:
repo:/regtest-results/activity1-activitypak-prime-only
- Result:
- run fell back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run fell back to the old settled-ocean hash:
- Important conclusion:
- the earlier repeated-literal stabilization effect is real, but it is not explained by simple one-shot priming alone
- the live seam is now narrower:
- something about the exact repeated lookup/use pattern matters
- not just “search ACTIVITY once before the normal path”
- Cleanup:
- reverted the temporary prime-only prelookup
- Next target:
- compare the successful double-literal pattern against the failing prime-only pattern more directly
- likely focus on:
- which
CdlFILEinstance is later reused - and whether avoiding the final
CdSearchFile(&cdfile, cdPath)entirely changes the path
- which
01:45 PDT - Reusing a known-good literal CdlFILE still does not stabilize the normal path
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- performed:
CdSearchFile(&primeFile, "\\PACKS\\ACTIVITY.PAK;1")
- then skipped the normal
CdSearchFile(&cdfile, cdPath)by copying:cdfile = primeFile
- the rest of the pack-index path was unchanged
- performed:
- in
- Validation run:
repo:/regtest-results/activity1-activitypak-reuse-primefile
- Result:
- run still fell back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run still fell back to the old settled-ocean hash:
- Important conclusion:
- the earlier repeated-literal stabilization effect is narrower than:
- one-shot priming
- or reusing a known-good
CdlFILEdirectly
- so the remaining seam is likely in the exact lookup/use sequence of the successful double-literal proof, not just in the final normal
CdSearchFile(...)call or theCdlFILEvalue alone
- the earlier repeated-literal stabilization effect is narrower than:
- Cleanup:
- reverted the temporary
cdfile = primeFileproof
- reverted the temporary
- Next target:
- compare the exact successful double-literal proof flow against the failing reuse flow
- likely inspect whether the success depended on:
- performing the second literal
CdSearchFile(...)into the same destination variable later used by the rest of the function
- performing the second literal
01:50 PDT - Double literal lookup without the old conditional still falls back
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- performed:
CdSearchFile(&primeFile, "\\PACKS\\ACTIVITY.PAK;1")CdSearchFile(&cdfile, "\\PACKS\\ACTIVITY.PAK;1")
- then continued normally
- no early return, no mismatch check
- performed:
- in
- Validation run:
repo:/regtest-results/activity1-activitypak-doubleliteral-noearly
- Result:
- run still fell back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run still fell back to the old settled-ocean hash:
- Important conclusion:
- the earlier “successful” double-literal result was not a practical stabilizer sequence by itself
- it depended on the exact conditional proof structure around it, not just on executing two literal lookups
- that pushes the live seam back toward:
- subtle branch/control-flow perturbation from the proof itself
- rather than a reusable
CdSearchFileworkaround
- Cleanup:
- reverted the temporary double-literal-noearly probe
- Next target:
- compare the original successful conditional proof shape against the clean negative variants
- specifically isolate whether the old success came from:
- skipping later code via the conditional structure
- or changing register/stack/timing enough to mask the underlying fault
00:36 PDT - ACTIVITY ADS metadata is already wrong immediately after ps1_loadAdsData()
- Active scene/debug base:
- current
HEAD - exact scene:
ACTIVITY.ADS 1
- current
- Strong parser-side results established this session:
- exact ACTIVITY entry offset is still correct after
adsLoad(...):offset == 2
- but these invariants do not hold on the PS1 runtime path:
adsNumTags == 10numAdsChunks == 6adsFindTag(12) == 176
- exact ACTIVITY entry offset is still correct after
- New earlier proof:
- in
repo:/ads.c, immediately after the PS1 lazy ADS load block and before:- pilot priming
pinResource(...)checkMemoryBudget()adsLoad(...)
- for exact
ACTIVITY.ADS 1, added a guard:- if
adsResource->uncompressedSize != 2558 - call:
adsReleaseIsland()adsNoIsland()return
- if
- in
- Validation run:
repo:/regtest-results/activity1-ads-size-early-proof
- Result:
frame_04920stayed on the improved black lead-in:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- late frames changed to the same alternate persistent state seen in the earlier byte-proof:
frame_06000:81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03frame_06150:81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03frame_06300:81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03
- Important conclusion:
- the ACTIVITY ADS corruption is earlier than:
pinResource(...)checkMemoryBudget()adsLoad(...)
- by the time
ps1_loadAdsData()returns, exactACTIVITY.ADS 1already has the wronguncompressedSize - that shifts the live root-cause surface into:
- PS1 ADS load path
- or ADS resource metadata integrity before/inside
ps1_loadAdsData()
- the ACTIVITY ADS corruption is earlier than:
- Cleanup:
- reverted the temporary early-size proof patch after validation
- Next target:
- inspect:
ps1_loadAdsData(...)- ADS metadata parse path in
resource.c - any mismatch between authored ACTIVITY metadata (
uncompressedSize = 2558) and runtime PS1 metadata/load behavior
- inspect:
23:55 PDT - Older 6dae8410 base is not a trustworthy post-boot PS1 scene-debug target
- Active validation base:
- detached worktree at
/tmp/jc_reborn_6dae - commit:
6dae8410Fix PS1 first-handoff blackscreen
- detached worktree at
- What I validated:
- the old-base regtest harness really is staging the requested boot override into the snapshot disc:
strings -a regtest-results/activity4-6dae-directads4/disc/jcreborn.bin- contains:
island ads ACTIVITY.ADS 4
- the old-base PS1 executable really is changing between proof runs:
activity4-6dae-ps1load-entry-trap2:Hash for 'JCREBORN.EXE' - 6A2E4F25CCD7CF0E
activity4-6dae-main-entry-trap:Hash for 'JCREBORN.EXE' - 6880449D0B51BA34
- the old-base regtest harness really is staging the requested boot override into the snapshot disc:
- Strong proofs that failed to fire despite different executables:
- temporary non-optimizable infinite-loop trap at the top of:
ps1LoadBootOverride()
- validation run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-ps1load-entry-trap2
- result:
- exited with success
Frames dumped: 96Total execution time: 2464.34ms
- temporary non-optimizable infinite-loop trap at the very top of:
main()
- validation run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-main-entry-trap
- result:
- exited with success
Frames dumped: 96Total execution time: 3811.94ms
- temporary non-optimizable infinite-loop trap at the top of:
- Important conclusion:
- on
6dae8410, the captured PS1 run is not behaving like a trustworthy post-main()game execution target - the disc snapshot does contain the requested
BOOTMODE.TXToverride - but even hard traps at
ps1LoadBootOverride()entry andmain()entry do not stall execution - therefore this older base is not a defensible target for deeper scene-by-scene ADS debugging above pre-
main()startup
- on
- Cleanup:
- reverted the temporary
ps1LoadBootOverride()andmain()trap probes after validation
- reverted the temporary
- Next target:
- stop using
6dae8410as the primary scene-debug base - choose a newer PS1 snapshot that actually reaches normal runtime code paths, then continue one-scene-at-a-time from there
- stop using
00:05 PDT - Returned to current HEAD; ACTIVITY 1 remains the live scene target
- What I validated:
- the real PS1-side
adsTagslifetime fix is still present inrepo:/ads.c:- PS1 path uses:
ps1StaticAdsTags
- instead of per-play heap allocation
- PS1 path uses:
- the earlier improved ACTIVITY result artifacts are still the right baseline:
repo:/regtest-results/activity1-ads1-seed1-static-adstags-fix/result.jsonrepo:/regtest-results/activity1-ads1-seed1-static-adstags-fix-6300/result.json
- the real PS1-side
- Current ACTIVITY 1 read on
HEAD:- the old settled bootstrap collapse is gone
- but the scene is still not correct
- by
6300frames the run settles to:screen_type = oceanisland_present = falsejohnny_present = falselaunched = falsebmp_ok = truesprite_count_estimate = 63
- strongest artifact:
repo:/regtest-results/activity1-ads1-seed1-static-adstags-fix-6300/20260328-215318/frames/jcreborn/frame_06300.png
- Harness note:
- a fresh compare-wrapper rerun for
ACTIVITY 1on currentHEADcompleted the PS1 capture phase but hung in post-capture compare generation - raw scene artifacts were produced under:
repo:/regtest-results/reference-compare-current-activity1/activity-1
- but no final
compare.json/compare.htmlwas emitted before the wrapper was stopped - that is a secondary harness issue, not the active scene root cause
- a fresh compare-wrapper rerun for
- Important conclusion:
6dae8410is out- current
HEADis back to being the only defensible live PS1 scene-debug base - active scene remains:
ACTIVITY 1
- active bug surface remains:
- exact ACTIVITY bootstrap no longer fully collapses
- but later live playback still fails to take ownership of the scene, leaving a bad late ocean state
- Next target:
- continue on
ACTIVITY 1from currentHEAD - debug the post-bootstrap playback path after the static-
adsTagsfix, not the older pre-main()validation base
- continue on
00:12 PDT - Current ACTIVITY 1 still crosses the old pre-chunk slot-1 corruption seam
- Temporary proof:
- in
repo:/ads.c, immediately after the exactACTIVITY.ADS 1TTM load loop and before the firstadsPlayChunk(...), added a temporary guard:- if
ttmSlots[1].ttmResourcewas notGJDIVE.TTM - call:
adsReleaseIsland()adsNoIsland()unpinResource(adsResource, "ADS")return
- if
- in
- Validation run:
repo:/regtest-results/activity1-slot1-prechunk-proof
- Result:
frame_04920stayed on the post-adsTagsimproved black lead-in:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- but late frames regressed back to the old settled-ocean hash:
frame_06000:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fcframe_06150:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fcframe_06300:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- Important conclusion:
- the static
adsTagsfix did move the bootstrap behavior - but current
ACTIVITY 1still crosses the earlier pre-chunk slot/resource corruption seam - the strongest live suspect remains:
- slot
1resource integrity between:- the
ttmLoadTtm()loop - and the first exact
adsPlayChunk(...)
- the
- slot
- the static
- Cleanup:
- reverted the temporary slot-1 prechunk proof patch after validation
- Next target:
- inspect what mutates or invalidates
ttmSlots[1]in that narrow pre-chunk window on currentHEAD
- inspect what mutates or invalidates
00:18 PDT - ACTIVITY 1 slot-1 corruption is already present by the time adsLoad() returns
- Temporary proof:
- in
repo:/ads.c, immediately after:adsLoad(data, dataSize, adsResource->numTags, adsTag, &offset);
- for exact
ACTIVITY.ADS 1, added a temporary guard:- if
ttmSlots[1].ttmResourceis notGJDIVE.TTM - call:
adsReleaseIsland()adsNoIsland()unpinResource(adsResource, "ADS")return
- if
- in
- Validation run:
repo:/regtest-results/activity1-post-adsload-slot1-proof
- Result:
frame_04920stayed on the improved black lead-in:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- late frames changed to a different persistent state:
frame_06000:81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03frame_06150:81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03frame_06300:81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03
- Important conclusion:
- this guard still perturbs the long-run ACTIVITY outcome
- therefore the slot/resource corruption seam is already crossed by the time
adsLoad()returns - that moves the active root-cause surface earlier than:
- first exact
adsPlayChunk(...)
- first exact
- strongest live suspect now:
adsLoad(...)itself, or state it mutates while parsing tags/chunks
- Cleanup:
- reverted the temporary post-
adsLoad()proof patch after validation
- reverted the temporary post-
- Next target:
- inspect
adsLoad(...)for side effects that can invalidate slot/resource state on currentHEAD
- inspect
00:25 PDT - adsLoad() runtime tag table no longer preserves authored ACTIVITY startup tag 12 -> 176
- Offline authored fact:
- extracted
repo:/jc_resources/extracted/ads/ACTIVITY.ADSparses to:10tags total- tag
1offset2 - first-chunk bookmarked globals
= 6 - tag
12offset176 - tag
11offset364
- extracted
- Temporary proof:
- in
repo:/ads.c, immediately afteradsLoad(...), for exactACTIVITY.ADS 1, added a guard:- if
adsFindTag(12) != 176 - call:
adsReleaseIsland()adsNoIsland()unpinResource(adsResource, "ADS")return
- if
- in
- Validation run:
repo:/regtest-results/activity1-tag12-proof
- Result:
frame_04920stayed on the improved black lead-in:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- late frames regressed to the old settled-ocean hash:
frame_06000:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fcframe_06150:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fcframe_06300:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- Important conclusion:
- this guard clearly fired
- so after runtime
adsLoad()on currentHEAD, the exact ACTIVITY startup tag table is already wrong enough that:adsFindTag(12) != 176
- this is stronger than the earlier bundled parse proof:
- the corruption is not just “some parse invariant”
- it directly affects the authored startup tag lookup needed to bootstrap ACTIVITY
- Cleanup:
- reverted the temporary
adsFindTag(12)proof patch after validation
- reverted the temporary
- Next target:
- distinguish whether runtime
adsFindTag(12)is:- missing entirely (
0) - or present but wrong offset
- missing entirely (
- distinguish whether runtime
00:31 PDT - ACTIVITY.ADS parse-time metadata is still correct; corruption happens later
- Temporary proof:
- in
repo:/ads.c, moved the exact-size guard earlier so it checked:adsResource->uncompressedSize != 2558
- before calling
ps1_loadAdsData(...) - then moved the same proof one step earlier again into
repo:/cdrom_ps1.cps1_parseAdsResource(...):- if exact
ACTIVITY.ADSparsed withuncompressedSize != 2558 - return
NULL
- if exact
- in
- Validation runs:
repo:/regtest-results/activity1-preload-metadata-proofrepo:/regtest-results/activity1-parse-size-proof
- Result:
- pre-load metadata proof changed the run back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- parse-time proof did not fire; run stayed on the improved post-
adsTagsbranch:91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534d
- pre-load metadata proof changed the run back to the old settled-ocean hash:
- Important conclusion:
ACTIVITY.ADSresource metadata parses correctly from the resource table- the bad
uncompressedSizeappears later, after resource-table build but before first visible ACTIVITY playback
- Cleanup:
- reverted the temporary parse-time proof after validation
- Next target:
- split the lazy-load path:
- pilot-pack ADS load
- raw-file fallback
- split the lazy-load path:
00:39 PDT - The bad size comes from the pilot-pack ADS load path, not from authored pack content
- Temporary proof:
- in
repo:/cdrom_ps1.cps1_loadAdsData(...), for exactACTIVITY.ADS:- if
ps1PilotLoadResource("ads", ...)succeeds but returnsreadSize != 2558 - drop the loaded pointer and return
- if
- in
- Validation run:
repo:/regtest-results/activity1-pilotload-size-proof
- Result:
- run changed back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run changed back to the old settled-ocean hash:
- Important conclusion:
- the proof fired on the pilot-pack success path
- so the bad ACTIVITY size is being sourced by
ps1PilotLoadResource(...) - it is not explained by parse-time metadata and does not require the raw-file fallback path
- Additional validation:
- the authored on-disk pack is clean:
repo:/jc_resources/packs/ACTIVITY.PAK
- offline header parse shows:
ACTIVITY.ADSentry size2558
- the authored on-disk pack is clean:
- Cleanup:
- reverted the temporary pilot-load proof after validation
- Next target:
- determine whether the in-memory pack index is already wrong when loaded, or only becomes wrong later in pack-entry lifetime
00:45 PDT - Runtime ACTIVITY.PAK index handling is still the live seam
- Temporary proofs:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...):- if exact
ACTIVITY.ADSpack index decodes anads/ACTIVITY.ADSentry withsizeBytes != 2558 - return
0
- if exact
- then tried two read-path variants for exact
ACTIVITY.ADSpack index load:- whole-file read for the larger second header/index read only
- whole-file read for both pack-header reads
- in
- Validation runs:
repo:/regtest-results/activity1-packindex-size-proofrepo:/regtest-results/activity1-packindex-wholeproofrepo:/regtest-results/activity1-packheader-wholeproof
- Result:
- all three cuts still land on the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- all three cuts still land on the old settled-ocean hash:
- Important conclusion:
- the on-disk pack header is correct, but the runtime pack-index path for
ACTIVITY.PAKis still the live seam - switching the larger index read, or both header reads, to the whole-file path did not recover the improved ACTIVITY branch
- so the remaining suspect space is:
- corrupted bytes returned by the runtime pack-index read path
- or later corruption of the active pack entry table after a correct decode
- the on-disk pack header is correct, but the runtime pack-index path for
- Cleanup:
- reverted the temporary whole-read pack-index proof patches after validation
- Next target:
- inspect active-pack table lifetime and ownership after
ps1PilotLoadPackIndex(...) - specifically whether
ps1PilotActivePack.entriesis being overwritten after pack activation forACTIVITY.ADS
- inspect active-pack table lifetime and ownership after
00:50 PDT - ps1PilotActivePack.entries is already wrong immediately after pack activation
- Temporary proof:
- in
repo:/cdrom_ps1.cps1_loadAdsData(...), for exactACTIVITY.ADS:- immediately after:
ps1PilotSetActivePackForAds(adsResource->resName);
- looked up:
ps1PilotFindEntry("ads", adsResource->resName)
- if the entry existed and
entry->sizeBytes != 2558- returned immediately before
ps1PilotLoadResource(...)
- returned immediately before
- immediately after:
- in
- Validation run:
repo:/regtest-results/activity1-activepack-entry-proof
- Result:
- run changed back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run changed back to the old settled-ocean hash:
- Important conclusion:
- the proof fired before
ps1PilotLoadResource(...)even ran - so the active-pack table is already wrong as soon as
ACTIVITY.ADSpack activation completes - the live seam is now earlier than ADS byte loading itself:
ps1PilotSetActivePackForAds(...)ps1PilotLoadPackIndex(...)- or stale/corrupted lifetime of
ps1PilotActivePack.entriesbefore this call returns
- the proof fired before
- Cleanup:
- reverted the temporary active-pack-entry proof after validation
- Next target:
- inspect pack activation and active-pack cache lifetime directly
- specifically whether:
ps1PilotSetActivePackForAds(...)is reusing a staleps1PilotActivePack- or
ps1PilotLoadPackIndex(...)populates a bad in-memory table even though the on-disk pack is correct
00:55 PDT - Cold-resetting active/prefetch packs does not recover ACTIVITY 1
- Temporary proof:
- in
repo:/cdrom_ps1.cps1_loadAdsData(...), for exactACTIVITY.ADS:- before
ps1PilotSetActivePackForAds(...), forcibly:ps1PilotResetActivePack();ps1PilotResetPack(&ps1PilotPrefetchPack);
- before
- in
- Validation run:
repo:/regtest-results/activity1-coldpack-proof
- Result:
- run still changed back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run still changed back to the old settled-ocean hash:
- Important conclusion:
- stale active/prefetch cache reuse is not sufficient to explain the bad ACTIVITY pack entry
- even a cold activation path still produces the wrong in-memory
ACTIVITY.PAKtable on currentHEAD - the remaining live seam is now tighter:
- fresh
ps1PilotLoadPackIndex("ACTIVITY.ADS", ...) - or the CD read bytes feeding that fresh decode
- fresh
- Cleanup:
- reverted the temporary cold-pack proof after validation
- Next target:
- instrument fresh
ps1PilotLoadPackIndex(...)more directly - especially the decoded
entryCount/ACTIVITY.ADSentry fields inside the newly allocatedentries[]table before it is installed as the active pack
- instrument fresh
01:00 PDT - Fresh ACTIVITY.PAK header parse is already wrong on the first 20-byte read
- Authored on-disk constants:
ACTIVITY.PAKheader decodes to:entryCount = 71firstResourceOffset = 2048prefetchCount = 1
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- after the first
PS1_PACK_HEADER_SIZEread and field decode - if any of:
entryCount != 71firstResourceOffset != 2048prefetchCount != 1
- return
0
- after the first
- in
- Validation run:
repo:/regtest-results/activity1-packheader-fields-proof
- Result:
- run changed back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run changed back to the old settled-ocean hash:
- Important conclusion:
- this proof fired before any entry-table decode
- so the very first 20-byte
ACTIVITY.PAKheader read is already wrong on the fresh runtime path - that rules out:
- stale pack reuse
- late active-pack entry corruption as the first cause
- entry-table decode as the earliest fault
- the live seam is now the pack-header CD read itself:
ps1_streamReadFromCdFile(&cdfile, 0, PS1_PACK_HEADER_SIZE)- or the
CdSearchFile/cdfilebasis feeding it
- Cleanup:
- reverted the temporary pack-header-fields proof after validation
- Next target:
- debug the
ACTIVITY.PAKheader CD read path directly - compare:
ps1_streamReadFromCdFile(...)ps1_streamReadFromCdFileWhole(...)- and raw whole-file CD reads for the first 20-byte pack header only
- debug the
01:05 PDT - Raw whole-file CD read for the first ACTIVITY.PAK header still fails the same way
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- replaced the first
PS1_PACK_HEADER_SIZEread with:ps1_loadRawFile("\\PACKS\\ACTIVITY.PAK;1", &rawPackSize)
- copied only the first 20 bytes into
headerData - then let normal header field decode continue
- replaced the first
- in
- Validation run:
repo:/regtest-results/activity1-packheader-rawproof
- Result:
- run still changed back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run still changed back to the old settled-ocean hash:
- Important conclusion:
- switching the first pack-header read from the range-read helper to the raw whole-file CD path did not recover the improved ACTIVITY branch
- so the earliest fault is now above the simple choice of:
- range read
- whole-file slice
- raw whole-file read
- the live seam is now likely:
CdSearchFile/ file-info basis forACTIVITY.PAK- or broader CD state/path handling before the first header bytes are interpreted
- Cleanup:
- reverted the temporary raw-header proof after validation
- Next target:
- inspect the
CdlFILE/ path / file-size basis forACTIVITY.PAKat pack activation time - specifically whether
CdSearchFileis resolving the expected pack file metadata before any read occurs
- inspect the
01:10 PDT - CdSearchFile metadata for ACTIVITY.PAK is already wrong before any header bytes are read
- Authored on-disk fact:
repo:/jc_resources/packs/ACTIVITY.PAK- size:
2836480bytes
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- immediately after:
CdSearchFile(&cdfile, "\\PACKS\\ACTIVITY.PAK;1")
- if:
cdfile.size != 2836480
- return
0
- immediately after:
- in
- Validation run:
repo:/regtest-results/activity1-pack-cdfile-proof
- Result:
- run changed back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run changed back to the old settled-ocean hash:
- Important conclusion:
- this proof fired before any header bytes were read
- so the earliest fault now exposed is
CdSearchFilemetadata forACTIVITY.PAK - that moves the live seam above:
- pack-header reads
- entry-table decode
- active-pack table lifetime
- strongest current suspect:
- path/file-resolution or disc-image lookup state for
PACKS\\ACTIVITY.PAK;1
- path/file-resolution or disc-image lookup state for
- Cleanup:
- reverted the temporary
cdfile.sizeproof after validation
- reverted the temporary
- Next target:
- inspect why
CdSearchFileresolves the wrongACTIVITY.PAKmetadata on currentHEAD - likely compare:
- exact path string
- disc image contents
- and any conflicting duplicate files or naming/case issues in the PS1 image build
- inspect why
01:16 PDT - CdSearchFile is not just returning the wrong size; the resolved ACTIVITY.PAK start sector is wrong too
- Built image fact:
- parsed directly from
repo:/jcreborn.bin - ISO entry:
PACKS/ACTIVITY.PAK;1- extent
3652 - size
2836480
- parsed directly from
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- immediately after
CdSearchFile(&cdfile, "\\PACKS\\ACTIVITY.PAK;1") - required both:
cdfile.size == 2836480CdPosToInt(&cdfile.pos) == 3652
- else returned
0
- immediately after
- in
- Validation run:
repo:/regtest-results/activity1-pack-cdfile-pos-proof
- Result:
- run still changed back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run still changed back to the old settled-ocean hash:
- Important conclusion:
- the earliest exposed runtime fault is now stronger than “wrong size” alone
CdSearchFileis resolvingACTIVITY.PAKwith metadata that does not match the built image’s actual ISO entry- that points the live seam at:
- path/file resolution state inside the PS1 runtime / emulator path
- or conflicting disc-image lookup behavior for
PACKS\\ACTIVITY.PAK;1
- Cleanup:
- reverted the temporary
cdfile.size + CdPosToInt(...)proof after validation
- reverted the temporary
- Next target:
- inspect path-resolution behavior for
PACKS\\ACTIVITY.PAK;1specifically - and compare against nearby known-good lookups to determine whether this is:
- a pack-path-specific lookup fault
- or a broader CD file-resolution problem
- inspect path-resolution behavior for
01:20 PDT - Resetting CD state immediately before ACTIVITY.PAK lookup does not recover the scene
- Temporary proof:
- in
repo:/cdrom_ps1.cps1PilotLoadPackIndex(...), for exactACTIVITY.ADS:- called:
cdromResetState()
- immediately before:
CdSearchFile(&cdfile, "\\PACKS\\ACTIVITY.PAK;1")
- called:
- in
- Validation run:
repo:/regtest-results/activity1-packsearch-reset-proof
- Result:
- run still changed back to the old settled-ocean hash:
59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run still changed back to the old settled-ocean hash:
- Important conclusion:
- stale CD state alone is not sufficient to explain the bad
ACTIVITY.PAKlookup - the live seam remains at or above the pack-specific
CdSearchFileresolution path itself
- stale CD state alone is not sufficient to explain the bad
- Cleanup:
- reverted the temporary reset-before-search proof after validation
- Next target:
- compare exact
CdSearchFileresolution behavior forACTIVITY.PAKagainst another known-good pack in the same image - to separate:
- ACTIVITY-pack-specific lookup failure
- from broader pack-directory/file-resolution corruption
- compare exact
22:58 PDT - ACTIVITY 4 on 6dae8410: story single was broken on PS1; now narrowed to exact-scene no-launch
- Goal:
- continue scene-by-scene PS1 validation from the older
6dae8410base and prove whetherACTIVITY 4was actually honoring the canonicalstory single 4boot path
- continue scene-by-scene PS1 validation from the older
- Context:
- older validation base:
/tmp/jc_reborn_6dae
- target scene:
ACTIVITY 4
- canonical boot string:
story single 4
- older validation base:
- What I found:
- in
/tmp/jc_reborn_6dae/jc_reborn.c, the PS1BOOTMODE.TXTparser only handled:story ads ...
- it silently ignored:
story single ...
- that explains the earlier PS1 drift into unrelated
WALKSTUF/FISHING-like content onACTIVITY 4
- in
- Fix applied in old worktree:
- added:
storySetBootSceneByIndex(int)in/tmp/jc_reborn_6dae/story.c
- declared it in:
/tmp/jc_reborn_6dae/story.h
- taught PS1 boot parsing to accept:
story single <index>- in
/tmp/jc_reborn_6dae/jc_reborn.c
- added:
- Validation run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-singlefix
- Result:
- the scene shape changed immediately and materially
- before fix:
- long drift into
WALKSTUF-looking ocean and then stableFISHING-like island content
- long drift into
- after fix:
- no unrelated later scene
- run stays in title / black
launched = falselikely_scene_not_started = truelikely_visual_broken = true
- this is useful progress:
- the old random-scene misroute was real
- the PS1 run is now exercising the intended exact
ACTIVITY 4path
- Follow-up check:
- ported the static
adsTagslifetime fix into the same old base:/tmp/jc_reborn_6dae/ads.c
- validation run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-singlefix-staticadstags
- ported the static
- A/B result:
- representative frame hashes were byte-identical before / after:
frame_00250frame_00310frame_00660frame_01200
- so the
adsTagsheap-lifetime bug that movedACTIVITY 1on currentHEADis not what blocksACTIVITY 4on6dae8410
- representative frame hashes were byte-identical before / after:
- Current read:
6dae8410remains the better PS1 validation baseACTIVITY 4no longer looks like a random story-family reroute- the active bug is now narrower:
- exact
ACTIVITY 4canonical boot path is requested - but it still fails as a no-launch / title-black bootstrap path on this older base
- exact
- Next target:
- debug the exact-scene bootstrap for
ACTIVITY 4after the now-correctstory single 4handoff, instead of chasing random later scene families
- debug the exact-scene bootstrap for
23:07 PDT - Direct ads ACTIVITY.ADS 4 matches story single 4 byte-for-byte on key frames
- Goal:
- determine whether the remaining
ACTIVITY 4failure on6dae8410is still in top-level story handoff, or already inside exactACTIVITY.ADS 4playback/bootstrap
- determine whether the remaining
- Validation run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-directads4- boot string:
island ads ACTIVITY.ADS 4
- Comparison target:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-singlefix- boot string:
story single 4
- Result:
- representative captured frames are byte-identical between the two runs:
frame_00310frame_00660frame_01200
- so the remaining
ACTIVITY 4failure is not in thestory singlewrapper anymore - it reproduces even when we bypass story selection and boot exact
ACTIVITY.ADS 4directly
- representative captured frames are byte-identical between the two runs:
- Important conclusion:
story singleparser bug was real and fixed- but the current blocker is now deeper:
- exact
ACTIVITY.ADS 4bootstrap / playback itself
- exact
- this is the right kind of narrowing for scene-by-scene PS1 debugging
- Next target:
- inspect the first exact
ACTIVITY.ADS 4bootstrap path directly:adsLoad(...)- first
adsPlayChunk(...) - startup
ADD_SCENE(...)thread creation / survival
- inspect the first exact
23:14 PDT - ACTIVITY.ADS 4 is not the same first-chunk zero-thread failure shape as ACTIVITY 1
- Goal:
- test whether exact
ACTIVITY.ADS 4on6dae8410is failing in the same wayACTIVITY 1did earlier:- first
adsPlayChunk(...)returns withnumThreads == 0
- first
- test whether exact
- Temporary proof:
- in
/tmp/jc_reborn_6dae/ads.c - immediately after the first:
adsPlayChunk(data, dataSize, offset);
- for exact:
ACTIVITY.ADS- tag
4
- if:
numThreads == 0- and
!adsStopRequested
- then:
adsNoIsland();return;
- in
- Validation run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-firstchunk-nolaunch
- Comparison target:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-directads4
- Result:
- representative frames remained byte-identical:
frame_00310frame_00660frame_01200
- so the temporary first-chunk no-launch hook did not fire in any way that changed visible behavior
- representative frames remained byte-identical:
- Important conclusion:
ACTIVITY 4on6dae8410is not currently failing with the same exact first-chunknumThreads == 0signature that previously helped pinACTIVITY 1- the remaining exact-scene bootstrap failure is subtler than that:
- either startup threads exist briefly and die later
- or the failure is happening on a different branch than the one just tested
- Cleanup:
- reverted the temporary first-chunk no-launch proof patch
- Next target:
- move one step deeper into exact
ACTIVITY.ADS 4startup:- inspect which startup
ADD_SCENE(...)calls actually execute - and whether those startup threads survive into the first main ADS loop iteration
- inspect which startup
- move one step deeper into exact
23:20 PDT - ACTIVITY.ADS 4 startup-chain proof is also a clean negative
- Goal:
- determine whether exact
ACTIVITY.ADS 4on6dae8410is reaching its authored startup chain in a way that affects the visible PS1 run
- determine whether exact
- Authored startup sequence extracted from
ACTIVITY.ADStag4:ADD_SCENE(2,1)IF_LASTPLAYED 2,1 -> ADD_SCENE(2,3)IF_LASTPLAYED 2,3 -> ADD_SCENE(2,2)
- Temporary proof:
- in
/tmp/jc_reborn_6dae/ads.c - added a trap in
adsAddScene(...) - for exact:
ACTIVITY.ADS- tag
4
- if startup scenes:
(2,1)(2,2)(2,3)- were being added
- then:
adsNoIsland();return;
- in
- Validation run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-startupchain-proof
- Comparison target:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-directads4
- Result:
- representative frames remained byte-identical:
frame_00310frame_00660frame_01200
- so this authored startup-chain trap did not move the visible run
- representative frames remained byte-identical:
- Important conclusion:
- on the current exact
ACTIVITY.ADS 4PS1 path, we are not reaching the authored(2,1) -> (2,3) -> (2,2)startup chain in any way that affects the visible outcome - combined with the earlier first-chunk no-launch negative, the live boundary moves earlier again:
- before visible startup thread creation
- likely in exact bootstrap resource/tag resolution or very-early chunk/control flow
- on the current exact
- Cleanup:
- reverted the temporary startup-chain trap after validation
- Next target:
- inspect the exact bootstrap state before visible startup
ADD_SCENE(...)activity:- tag resolution
- first-chunk control flow
- resource/slot binding for
ACTIVITY.ADS 4
- inspect the exact bootstrap state before visible startup
23:31 PDT - Post-ttmLoadTtm() slot-2 visual proofs are also negative for ACTIVITY.ADS 4
- Goal:
- test whether exact
ACTIVITY.ADS 4on6dae8410has an obviously wrong slot-2 resource binding immediately after the TTM load loop
- test whether exact
- Context:
- authored startup for
ACTIVITY.ADStag4only touches slot2 - so slot
2is the highest-value binding to probe first
- authored startup for
- Temporary proofs tried in
/tmp/jc_reborn_6dae/ads.cimmediately after:ttmLoadTtm(&ttmSlots[adsResource->res[i].id], adsResource->res[i].name);
- Proof variants:
- if slot
2is correctly bound toMJDIVE.TTM, force a visible return - if slot
2is null / wrong, force a visibleNIGHT.SCRload plusgrUpdateDisplay(...)before return
- if slot
- Validation runs:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-slot2-bind-proof/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-slot2-nightproof/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-slot2-nullproof/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-slot2-nullproof2
- Comparison target:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-directads4
- Result:
- representative frames remained byte-identical across all variants:
frame_00310frame_00660frame_01200
- representative frames remained byte-identical across all variants:
- Important conclusion:
- the remaining exact
ACTIVITY.ADS 4failure is earlier than every post-ttmLoadTtm()visual proof tried so far - these probes did not produce a visible state change, so they are not good enough as the next proof surface
- current best boundary remains:
- exact ACTIVITY path is requested
- direct
ads ACTIVITY.ADS 4matchesstory single 4 - but the visible failure persists before any startup-thread or post-load resource proof has produced a perturbation
- the remaining exact
- Cleanup:
- reverted the temporary slot-2 visual proof patches
- Next target:
- move earlier than the post-load visual probes:
- exact tag-offset resolution in
adsLoad(...) - or pre-display bootstrap control flow that still leaves the title surface intact
- exact tag-offset resolution in
- move earlier than the post-load visual probes:
23:39 PDT - Even a top-of-adsPlay(ACTIVITY.ADS, 4) visible return is inert on the captured output
- Goal:
- verify whether the current title-like captured output is even a trustworthy proof surface for exact
ACTIVITY.ADS 4playback on6dae8410
- verify whether the current title-like captured output is even a trustworthy proof surface for exact
- Strong sanity-check proof:
- in
/tmp/jc_reborn_6dae/ads.c - at the top of exact:
adsPlay("ACTIVITY.ADS", 4)
- forced:
grLoadScreen("NIGHT.SCR")grUpdateDelay = 100grUpdateDisplay(NULL, ttmThreads, NULL)- immediate
return
- in
- Validation run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-adsplay-nightproof
- Comparison target:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-directads4
- Result:
- representative frames remained byte-identical:
frame_00310frame_00660frame_01200
- representative frames remained byte-identical:
- Important conclusion:
- the current title-like captured output is not a trustworthy proof surface for exact
ACTIVITY.ADS 4playback on this older base - after this many inert visible proofs, continuing to infer control flow from the stable title surface is no longer defensible
- the debugging strategy needs to change:
- stop relying on visible-frame perturbations at this boundary
- move to a different runtime-state proof surface
- the current title-like captured output is not a trustworthy proof surface for exact
- Cleanup:
- reverted the temporary top-of-
adsPlay()proof patch - reverted the temporary complementary tag-offset proof patch
- reverted the temporary top-of-
- Next target:
- switch
ACTIVITY 4debugging to a non-visual state surface on6dae8410 - likely candidates:
- runtime state hash / RAM hash transitions
- durable story / ADS state persisted into telemetry in a form that actually survives on this branch
- switch
23:45 PDT - Non-visual validation confirmed: exact adsPlay(ACTIVITY.ADS, 4) proof does execute even though frames do not move
- Goal:
- verify that the inert frame proofs on
ACTIVITY 4were not simply dead code paths
- verify that the inert frame proofs on
- Comparison:
- baseline direct exact ADS run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-directads4/printf.log
- top-of-
adsPlay(ACTIVITY.ADS, 4)proof run:/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-adsplay-nightproof/printf.log
- baseline direct exact ADS run:
- Result:
- captured frames stayed byte-identical, but the non-visual runtime surface changed substantially
- baseline:
Save State Hash = 048bcd0b774df0f7099975e33e26364b19cb2c6e3b6010e3a838b899b241d7c4RAM Hash = c492897409507dcb852cab1f8886ed64cc3efe94fa7341fd663ca207c1eb781aTotal execution time = 2545.77 ms
- proof run:
Save State Hash = de10700000100da320b4b3366e475d78a1b95c1571c864067f169d3b532a602cRAM Hash = 5c0939e6a6c10e1b2e8fa6e54f8de2973a651810f44b112e8569c172a49c3d91Total execution time = 3782.95 ms
VRAM Hashstayed the same
- Important conclusion:
- exact
ACTIVITY.ADS 4playback is executing on6dae8410 - the current captured frame surface is simply not trustworthy at this boundary
RAM Hash/Save State Hash/ execution-time changes are a better proof surface here than the PNGs
- exact
- Next target:
- continue
ACTIVITY 4using non-visual runtime-state proofs first - only use frames secondarily, once a proof is known to touch VRAM or display ownership
- continue
23:46 PDT - Fresh-build timeout proof shows island ads ACTIVITY.ADS 4 is not reaching main()’s argAds branch on 6dae8410
- Goal:
- verify the actual entrypoint for the old-base “direct ADS” runs, because earlier ADS-level timeout proofs were exiting cleanly even after fresh rebuilds
- Fresh-build check:
- touched
/tmp/jc_reborn_6dae/jc_reborn.c - confirmed rebuild picked it up:
jc_reborn.c.objjcreborn.exe- all rebuilt at
23:34
- touched
- Temporary proof:
- in
/tmp/jc_reborn_6dae/jc_reborn.c - at the top of:
else if (argAds && numArgs >= 2)
- inserted:
while (1) { }
- in
- Validation run:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-main-anyargads-timeoutproof- boot string:
island ads ACTIVITY.ADS 4
- timeout budget:
15s
- Result:
- run still exited cleanly
- it did not time out
- so the old-base direct-ADS boot string is not reaching
main()’sargAdsbranch
- Important conclusion:
- previous “direct ADS” runs on
6dae8410were not actually proving ADS-branch entry - that invalidates the earlier assumption that
island ads ACTIVITY.ADS 4was already inside exact ADS playback on this branch - the active root-cause surface moves up:
- PS1 boot-override parsing / retention / dispatch for
island ads ...
- PS1 boot-override parsing / retention / dispatch for
- previous “direct ADS” runs on
- Cleanup:
- reverted the temporary
argAdstimeout proof in/tmp/jc_reborn_6dae/jc_reborn.c - reverted the temporary
adsLoad(...)timeout proof in/tmp/jc_reborn_6dae/ads.c
- reverted the temporary
- Next target:
- inspect why
island ads ...is not reachingargAdson6dae8410 - specifically:
ps1LoadBootOverride()ps1ApplyBootOverride()argPlayAll/argAds/numArgsstate after parsing
- inspect why
21:54 PDT - adsLoad(...) heap allocation was corrupting ACTIVITY bootstrap; static ADS tag storage removes the old ocean failure
- First exact-bytes proof:
- temporary proof in
repo:/ads.cblacked only if the first exactACTIVITY.ADS 1load saw:dataSize == 2558- the expected extracted byte prefix
- validation:
repo:/regtest-results/activity1-ads1-seed1-ads-bytes-firstproof/20260328-214718/frames/jcreborn/frame_04910.png
- temporary proof in
- Result:
- boundary frames blacked:
frame_04900 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9frame_04910 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9frame_05000 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- so the first exact PS1
ACTIVITY.ADSpayload bytes are already correct beforeadsLoad(...)
- boundary frames blacked:
- Allocation-vs-scan split:
- replaced the temporary
adsTags = safe_malloc(...)allocation with a static scratch buffer only for the first exactACTIVITY.ADS 1load - blacked only if
ttmSlots[1].ttmResource == GJDIVE.TTMstill held immediately afteradsLoad(...) - validation:
repo:/regtest-results/activity1-ads1-seed1-adsload-static-tags/20260328-215017/frames/jcreborn/frame_04910.png
- replaced the temporary
- Result:
- the boundary frames blacked again
- so the active corruption surface was the
adsLoad(...)heap allocation path, not ACTIVITY byte delivery and not the ADS scan logic alone
- Current code change:
- converted PS1
adsTagsstorage inrepo:/ads.cfrom per-play heap allocation to a persistent static buffer with capacity guard:PS1_STATIC_ADS_TAG_CAPACITY = 1024
- converted PS1
- Clean validation run with the real change:
repo:/regtest-results/activity1-ads1-seed1-static-adstags-fix/20260328-215232/frames/jcreborn/frame_04910.png
- Clean longer validation:
repo:/regtest-results/activity1-ads1-seed1-static-adstags-fix-6300/20260328-215318/frames/jcreborn/frame_06000.png
- Important result:
- the old settled ACTIVITY ocean hash is gone
- boundary frames now stay black through the old failure onset:
frame_04920 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- later frames no longer match the old ocean either:
frame_06000 = 91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534dframe_06150 = 91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534dframe_06300 = 91d3e49ac1712390a97e7588519fa53d29b3a1cf33ae1f1696705a097ddb534d
- Current conclusion:
- this is the first material PS1 runtime fix on
ACTIVITY 1 - the
adsLoad(...)heap allocation really was corrupting bootstrap state - but
ACTIVITY 1is not fully fixed yet; it has moved from the old settled-ocean failure to a new later failure surface
- this is the first material PS1 runtime fix on
- Next target:
- characterize the new
06000+stable state after the static-adsTagsfix - then continue scene-by-scene debugging from that cleaner post-bootstrap runtime
- characterize the new
22:36 PDT - Step-back validation: 6dae8410 is a materially better PS1 base than current HEAD for ACTIVITY 4
- Reason for step-back:
- current
ps1HEAD had regressed too far for validation sweeps:ACTIVITY 1andACTIVITY 4were collapsing to black-screen/fallback behavior
- that made the current tree unsuitable as a “closest working” PS1 candidate
- current
- Validation base:
- older detached worktree:
/tmp/jc_reborn_6dae
- commit:
6dae84102026-03-21 09:02:06 -0700Fix PS1 first-handoff blackscreen
- older detached worktree:
- Harness bridge:
- copied current regtest harness scripts into the older worktree just for validation
- fixed that older snapshot’s stale
/tmp/jc_reborn_6dae/scripts/build-ps1.sh, which was still calling:make jcreborn
- updated it to use the existing CMake-based
build-ps1/tree
- One-scene validation:
- ran:
ACTIVITY 44200framesInterpreter
- output:
/tmp/jc_reborn_6dae/regtest-results/activity4-6dae-check/result.json
- ran:
- Result:
- older
6dae8410snapshot reaches real non-black scene content - last visual scene on that run:
screen_type = islandscene_family = FISHINGjohnny_present = truesand_present = true
- hashes:
state_hash = c979c7cb2a17e672791991117ed14777f3441fe6b074cd69f5683189d7435167- best scene frame:
frame_03150.pngframe_scene_pixel_sha256 = 76de45bc2265ba5180709b2273f5a5f655459a3072d30b27d45c23de9f195426
- last scene frame:
frame_04200.pngframe_scene_pixel_sha256 = 54a1f25fab3e3fa0ae236af60b06c022d556d8bdd686cfedff8104e1f860b9bf
- older
- Important conclusion:
- even before the exact compare JSON is finalized, this is already enough to choose direction:
6dae8410is materially healthier than current HEAD for one-scene validation- current HEAD should not be used as the candidate PS1 sweep/debug base
- Next target:
- keep validating from
6dae8410one scene at a time - confirm the exact
ACTIVITY 4compare verdict when the compare tool completes - if that remains the best behavior, continue scene debugging from the older base instead of current HEAD
- keep validating from
21:20 PDT - ACTIVITY startup failure is now narrowed to missing slot-1 resource binding before first chunk playback
- Temporary proof family in
repo:/ads.c:- black-and-return only for exact
ACTIVITY.ADS 1under progressively narrower conditions - all probes were run under
--cpu Interpreterto avoid DuckStation recompiler crashes
- black-and-return only for exact
- Validation runs:
repo:/regtest-results/activity1-ads1-seed1-startup-survival-proof-interp/20260328-211458/frames/jcreborn/frame_04910.pngrepo:/regtest-results/activity1-ads1-seed1-startup-zeroip-only/20260328-211613/frames/jcreborn/frame_04910.pngrepo:/regtest-results/activity1-ads1-seed1-startup-slot1-ttmproof/20260328-211819/frames/jcreborn/frame_04910.pngrepo:/regtest-results/activity1-ads1-seed1-startup-slot1-nullproof/20260328-211918/frames/jcreborn/frame_04910.pngrepo:/regtest-results/activity1-ads1-seed1-slot1-prechunk-nullproof/20260328-212046/frames/jcreborn/frame_04910.pngrepo:/regtest-results/activity1-ads1-seed1-slot1-resource-list-proof/20260328-212156/frames/jcreborn/frame_04910.png
- Result:
- every proof above drove
frame_04900,frame_04910, andframe_05000to the same black hash:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- every proof above drove
- Important conclusions:
- the first exact ACTIVITY chunk really does reach authored startup
ADD_SCENE(1,12)/ADD_SCENE(1,13) - a zero-
ipbirth proof alone is sufficient to black out the stable ocean band - by
adsAddScene(1,12/13), slot1is not bound to liveGJDIVE.TTM - a null-only slot-1 proof also blacks the band, so this is not just “wrong non-null TTM”; slot
1is effectively unbound - moving the proof earlier, immediately after the
ttmLoadTtm()loop and before first-chunk playback, still blacks the band - finally, proving only that the parsed ACTIVITY resource list lacks
slot 1 -> GJDIVE.TTMalso blacks the band
- the first exact ACTIVITY chunk really does reach authored startup
- Current best read:
- the ACTIVITY failure is now upstream of scene-thread lifetime
- on PS1, the parsed
ACTIVITY.ADSresource mapping reaching runtime does not preserve the expectedslot 1 -> GJDIVE.TTMbinding - that explains why authored startup tags
12/13becomeip == 0at runtime and no ACTIVITY startup thread survives first-chunk bootstrap
- Cleanup:
- reverted the temporary proof patches after validation; only the worklog entry remains
- Next target:
- inspect why PS1
ACTIVITY.ADSresource metadata losesslot 1 -> GJDIVE.TTM - likely surfaces:
- PS1 ADS resource parse/list integrity
- ACTIVITY resource-list contents at runtime versus expected authored mapping
- any slot reset/overwrite before first chunk playback
- inspect why PS1
21:35 PDT - Raw RESOURCE.001 parse confirms ACTIVITY source asset is correct
- Parsed the on-disk
ACTIVITY.ADSentry directly from:repo:/jc_resources/RESOURCE.MAPrepo:/jc_resources/RESOURCE.001
- Important detail:
- the ADS
RES:table uses variable-length NUL-terminated names, matchinggetString(..., 40)/ps1_getString(..., 40), not fixed 40-byte records
- the ADS
- Direct raw result for
ACTIVITY.ADS:numRes = 6slot 1 -> GJDIVE.TTMslot 2 -> MJDIVE.TTMslot 4 -> MJREAD.TTMslot 5 -> MJBATH.TTMslot 6 -> GJNAT1.TTMslot 7 -> GJNAT3.TTM
- Important conclusion:
- the source asset on disk is correct
- the failure is not in authored ACTIVITY metadata
- the remaining bug surface is after bytes leave disk:
- PS1 runtime parse/in-memory ADS resource state
- or later slot reset/overwrite before first chunk playback
- Cleanup:
- reverted an unstable first-entry positive proof patch after it failed to produce a clean capture window
- Next target:
- inspect live in-memory
adsResource->numRes/res[]state on the PS1 path without relying on repeated-entry proofs - then inspect whether any slot reset occurs between
ttmLoadTtm()and the first startupadsAddScene(1,12/13)
- inspect live in-memory
21:45 PDT - First exact ACTIVITY in-memory ADS list and slot-1 TTM load are both correct
- Temporary first-entry proofs in
repo:/ads.c, all under--cpu Interpreter:- black-and-return only if the first exact
adsPlay("ACTIVITY.ADS", 1)sees the full expected six-entry in-memoryadsResource->res[]mapping - then black-and-return only if that same first exact path sees
ttmSlots[1].ttmResource == GJDIVE.TTMimmediately after thettmLoadTtm()loop
- black-and-return only if the first exact
- Validation runs:
repo:/regtest-results/activity1-ads1-seed1-reslist-firstproof/20260328-213640/frames/jcreborn/frame_04910.pngrepo:/regtest-results/activity1-ads1-seed1-slot1-load-firstproof/20260328-213740/frames/jcreborn/frame_04910.png
- Result:
- both proofs turned
frame_04900,frame_04910, andframe_05000black:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- both proofs turned
- Important conclusion:
- on the first exact
ACTIVITY.ADS 1playback:- the in-memory ADS resource list is already correct
- and
ttmSlots[1]is correctly bound toGJDIVE.TTMimmediately after the load loop
- so the slot-1 loss does not happen in:
- source asset metadata
- PS1 ADS resource parsing
- or the initial
ttmLoadTtm()binding step
- the remaining live boundary is now very tight:
- between the end of the
ttmLoadTtm()loop and the first startupadsAddScene(1,12/13)
- between the end of the
- on the first exact
- Cleanup:
- reverted the temporary first-entry proof patches after validation
- Next target:
- inspect what mutates or invalidates slot
1in that narrow pre-chunk bootstrap span - likely surfaces:
adsLoad(...)adsPlayChunk(...)- or a slot reset/overwrite side effect before
adsAddScene(1,12/13)runs
- inspect what mutates or invalidates slot
21:55 PDT - adsLoad(...) is the active boundary, but not via simple tag/chunk table overflow
- Temporary boundary proof in
repo:/ads.c:- on the first exact
ACTIVITY.ADS 1playback only - black-and-return immediately after
adsLoad(...)ifttmSlots[1]is already not bound toGJDIVE.TTM
- on the first exact
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-prechunk-slot1-loss/20260328-214146/frames/jcreborn/frame_04910.png
- Result:
frame_04900,frame_04910,frame_05000all black:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- Important conclusion:
- the slot-1 loss is already present immediately after
adsLoad(...) - so the current corruption boundary is:
- after
ttmLoadTtm()succeeds forslot 1 -> GJDIVE.TTM - but before the first
adsPlayChunk(...)executes ACTIVITY bytecode
- after
- the slot-1 loss is already present immediately after
- Follow-up static validation using
repo:/jc_resources/extracted/ads/ACTIVITY.ADS:- simulated the exact
adsLoad(...)scanner on the real decompressed ACTIVITY bytecode - observed:
adsNumTags = 10, matching metadata fromRESOURCE.001- bookmarked chunk counts stay well below
MAX_ADS_CHUNKS
- simulated the exact
- Important refinement:
- this is not explained by a simple
adsTagsoverflow - and not by a simple
adsChunksoverflow adsLoad(...)remains the active boundary, but the failure is subtler than straightforward table overrun from ACTIVITY bytecode shape
- this is not explained by a simple
- Cleanup:
- reverted the temporary post-
adsLoad(...)proof patch after validation
- reverted the temporary post-
- Next target:
- inspect heap/object lifetime side effects inside
adsLoad(...) - especially:
adsTags = safe_malloc(...)adsReleaseAds()/free(adsTags)lifetime assumptions- any corruption caused by repeated ACTIVITY exact-entry bookkeeping versus allocator reuse
- inspect heap/object lifetime side effects inside
20:16 PDT - Quadrant-color upload proof collapsed the late ACTIVITY frames to near-black, but did not identify tile ownership
- Temporary proof:
- in
repo:/graphics_ps1.c, replaced the normal lategrDrawBackground()upload source for exactACTIVITY.ADS 1during04900..04910 - each quadrant tile uploaded from a different solid-color buffer:
- top-left: red
- top-right: green
- bottom-left: blue
- bottom-right: white
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-quadrant-proof-4900-4910
- Result:
- late sampled frames all collapsed to the same new hash:
frame_04900 = 1c7b028eeb1335dc37111468c89ff4c9f2194efe3645fae8656d5114bebf52b5frame_04910 = 1c7b028eeb1335dc37111468c89ff4c9f2194efe3645fae8656d5114bebf52b5frame_05000 = 1c7b028eeb1335dc37111468c89ff4c9f2194efe3645fae8656d5114bebf52b5
- simple quadrant averages on
frame_04910were all essentially black:- top-left:
(0, 0, 1) - top-right:
(0, 0, 0) - bottom-left:
(0, 0, 0) - bottom-right:
(0, 0, 0)
- top-left:
- late sampled frames all collapsed to the same new hash:
- Important conclusion:
- this was not informative enough to identify which tile/quadrant owns the settled ACTIVITY ocean
- the proof only showed that substituting the late upload source in this way can collapse the frame to near-black
- the next useful surface remains the real normal upload-source content at
04910, not another quadrant substitution
- Cleanup:
- reverted the temporary quadrant-color proof patch after validation
- Next target:
- inspect or checksum the real
grDrawBackground()tile-upload source content at the04910handoff window
- inspect or checksum the real
20:28 PDT - By 04900..05000, no normal background-tile uploads are active anymore on the stable ACTIVITY failure path
- Temporary telemetry:
- in
repo:/graphics_ps1.c, added compact-strip values for:- per-tile
grDrawBackground()upload source signatures - an upload-active bitmask showing which of the four background tiles actually reached
LoadImage(...)this frame
- per-tile
- decoded them in
repo:/scripts/decode-ps1-bars.py
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-uploadsig2
- Decoded boundary frames:
frame_04900:upload_active_mask_estimate = 0- all four
upload_sig_tile* = 0
frame_04910:upload_active_mask_estimate = 0- all four
upload_sig_tile* = 0
frame_05000:upload_active_mask_estimate = 0- all four
upload_sig_tile* = 0
- Important conclusion:
- by the settled bad-ocean band,
grDrawBackground()is no longer performing any normal per-tile uploads at all - that reconciles the earlier renderer proofs:
- forcing a black upload source only changes the result when uploads are explicitly forced into the band
- otherwise the visible ACTIVITY ocean is already persistent before
04900
- so the active boundary is now earlier than the late renderer upload path
- the next useful target is no longer “what gets uploaded at
04910,” but:- where the last successful background upload happens before the ocean becomes persistent
- and why no later ACTIVITY scene content dirties/uploads the framebuffer after that point
- by the settled bad-ocean band,
- Next target:
- move the renderer proof surface earlier than
04900 - identify the final frame where any background tile upload is still active, then debug the transition from that last upload into the settled persistent ocean
- move the renderer proof surface earlier than
20:33 PDT - The last decoded normal background upload in the ACTIVITY run is back at frame_0610
- Follow-up on
repo:/regtest-results/activity1-ads1-seed1-uploadsig2:- scanned the full decoded telemetry timeline for the new:
upload_active_mask_estimateupload_sig_tile*
- scanned the full decoded telemetry timeline for the new:
- Result:
- the last frame with any nonzero decoded upload activity is:
frame_0610
- from
frame_0620onward,upload_active_mask_estimate = 0 - that includes the entire settled failure band:
frame_04900frame_04910frame_05000
- the last frame with any nonzero decoded upload activity is:
- Important conclusion:
- the stable ACTIVITY ocean is not being maintained by normal
grDrawBackground()uploads anywhere near the failure onset - by the time the run reaches the long black lead-in and then the settled
04910+ocean, the normal background upload path is already dormant - that is consistent with the earlier blackproofs:
- forced uploads can overwrite the persistent framebuffer
- but the normal path is no longer uploading there
- the stable ACTIVITY ocean is not being maintained by normal
- Current read:
- the active bug is now better described as:
- normal background upload activity stops very early
- then some other path leaves or restores a persistent ocean/title-derived visible framebuffer state
- and no later ACTIVITY scene content ever dirties/uploads over it
- the active bug is now better described as:
- Next target:
- stop treating
04910as an active upload site - debug the direct framebuffer/background ownership path after uploads stop:
- title/intro display persistence
- direct screen loads
- any display-page reuse that survives while the ACTIVITY scene never reclaims the framebuffer
- stop treating
20:43 PDT - One-shot post-init framebuffer clear removes the stale 04900 title, but not the settled 04910+ ocean
- Temporary proof:
- in
repo:/jc_reborn.c, for exactACTIVITY.ADS 1only:- immediately after
graphicsInit()and palette setup - called:
grInitEmptyBackground()grDrawBackground()VSync(0)
- immediately after
- this performs a one-shot black framebuffer handoff after the early title path, before
storyPlay()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-postinit-clear2
- Result:
frame_04900changed from the old late-title frame to black:- new hash:
0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- new hash:
- but the settled ACTIVITY ocean did not move:
frame_04910 = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fcframe_05000 = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- Important conclusion:
- there really is stale pre-story framebuffer/title persistence contributing to the late
04900frame - but that is not the root cause of the settled ACTIVITY failure
- the later
04910+ocean is being established independently of that stale title residue
- there really is stale pre-story framebuffer/title persistence contributing to the late
- Cleanup:
- reverted the temporary post-init framebuffer-clear proof patch after validation
- Next target:
- treat
04900title persistence and04910+settled ocean as two separate phenomena - continue debugging the first point where the persistent ACTIVITY ocean itself is established
- treat
20:46 PDT - The settled 04910+ ACTIVITY ocean comes directly from the exact OCEAN0?.SCR bootstrap load in islandInit()
- Temporary proof:
- in
repo:/island.c, for exactACTIVITY.ADS 1only:- replaced only the initial:
grLoadScreen("OCEAN0?.SCR")
- with:
grInitEmptyBackground()
- replaced only the initial:
- left the rest of
islandInit()intact:- raft/cloud/background BMP loads
- island sprite composition
- initial wave draws
- later ACTIVITY flow unchanged
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-skip-ocean-load
- Result:
- all three boundary frames turned black:
frame_04900 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9frame_04910 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9frame_05000 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- all three boundary frames turned black:
- Important conclusion:
- this is the strongest causal proof so far:
- the settled
04910+ACTIVITY ocean is coming directly from the exact bootstrapOCEAN0?.SCRload inislandInit() - it is not being independently recreated later by other island composition or fallback logic
- the settled
- that means the remaining ACTIVITY failure is now best described as:
- the bootstrap ocean background loads successfully
- but the intended ACTIVITY scene content never later reclaims or replaces it
- this is the strongest causal proof so far:
- Cleanup:
- reverted the temporary skip-ocean-load proof patch after validation
- Next target:
- focus directly on why exact ACTIVITY playback never replaces the already-correctly-loaded bootstrap ocean background
20:52 PDT - Direct black-and-return at the top of storyPlayPreparedScene(ACTIVITY.ADS, 1) still does not move the settled 04910+ ocean
- Temporary proof:
- in
repo:/story.c, for exactACTIVITY.ADS 1only:- at the top of
storyPlayPreparedScene(scene, prevSpot, prevHdg) - called:
grInitEmptyBackground()grDrawBackground()
- then returned
0immediately
- at the top of
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-playprepared-blackreturn
- Result:
- lead-in changed:
frame_04900 = d9ee0d2e8f8465ea471c9c79223f1177c7225a238274f7f68ab32ed94312c0a0
- but the settled ACTIVITY ocean remained unchanged:
frame_04910 = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fcframe_05000 = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- lead-in changed:
- Important conclusion:
- this is another strong negative on the straightforward top-level playback path
- the settled
04910+ocean is still not being controlled by the obvious exactstoryPlayPreparedScene(ACTIVITY.ADS, 1)hook point - so the strongest remaining read is unchanged:
- exact bootstrap ocean background is loaded correctly
- but the later stable failure path that leaves it on screen is bypassing, outliving, or otherwise decoupling from the straightforward exact prepared-scene playback hook
- Cleanup:
- reverted the temporary
storyPlayPreparedScene()black-return proof patch after validation
- reverted the temporary
- Next target:
- move below top-level story hooks again
- identify the alternate ACTIVITY path that preserves the bootstrap ocean while the obvious exact prepared-scene playback hook remains behaviorally negative
20:56 PDT - Even returning from storyPlay() immediately after an exact no-launch still does not move the settled 04910+ ocean
- Temporary proof:
- in
repo:/story.c, wrapped:storyPlayPreparedScene(finalScene, prevSpot, prevHdg)
- if it returned false for exact
ACTIVITY.ADS 1, immediately:grInitEmptyBackground()grDrawBackground()return
- this bypassed the obvious retry loop after an exact prepared-scene no-launch
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-no-launch-return
- Result:
- lead-in changed:
frame_04900 = d9ee0d2e8f8465ea471c9c79223f1177c7225a238274f7f68ab32ed94312c0a0
- but the settled ACTIVITY ocean still did not move:
frame_04910 = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fcframe_05000 = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- lead-in changed:
- Important conclusion:
- the settled
04910+ACTIVITY ocean is still not explained by the obvious exact no-launch retry path instoryPlay() - this is another strong negative on the straightforward top-level story control flow
- the remaining failure path is still behaving as if it bypasses, outlives, or otherwise decouples from the obvious exact prepared-scene playback/no-launch branch
- the settled
- Cleanup:
- reverted the temporary no-launch-return proof patch after validation
- Next target:
- keep the focus below top-level story control flow
- the strongest remaining suspect surface is now the ADS bootstrap/scene-thread lifecycle itself, after the bootstrap ocean background has been loaded
21:01 PDT - If exact ACTIVITY still has numThreads == 0 immediately after the first adsPlayChunk(), the whole late boundary turns black
- Temporary proof:
- in
repo:/ads.c, for exactACTIVITY.ADS 1only:- immediately after the first:
adsPlayChunk(data, dataSize, offset)
- if
numThreads == 0, then:grInitEmptyBackground()grDrawBackground()return
- immediately after the first:
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-firstchunk-nolaunch-return
- Result:
- all three boundary frames turned black:
frame_04900 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9frame_04910 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9frame_05000 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- all three boundary frames turned black:
- Important conclusion:
- this is the strongest ADS bootstrap proof so far
- the settled ACTIVITY ocean is consistent with the exact first ADS chunk returning with no launched scene threads
- in other words:
- bootstrap ocean background loads
- first exact
adsPlayChunk()does not produce any running ACTIVITY scene thread - and later nothing reclaims the framebuffer from that bootstrap state
- Cleanup:
- reverted the temporary firstchunk-nolaunch-return proof patch after validation
- Next target:
- debug why the exact first
adsPlayChunk()path forACTIVITY.ADS 1leavesnumThreads == 0 - that is now the narrowest live root-cause surface
- debug why the exact first
21:06 PDT - The first exact ACTIVITY chunk definitely reaches authored startup ADD_SCENE(1,12) / ADD_SCENE(1,13)
- Temporary proof:
- in
repo:/ads.c, for exactACTIVITY.ADS 1only:- inside
adsAddScene(ttmSlotNo, ttmTag, arg3) - if the scene being added was authored startup slot/tag:
1:12or1:13
- then:
grInitEmptyBackground()grDrawBackground()return
- inside
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-addscene-init12-13-return
- Result:
- all three boundary frames turned black:
frame_04900 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9frame_04910 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9frame_05000 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- all three boundary frames turned black:
- Important conclusion:
- this proves the first exact ACTIVITY chunk really is reaching the authored startup
ADD_SCENEcalls for:slot 1 / tag 12slot 1 / tag 13
- combined with the earlier first-chunk no-launch proof, the remaining root-cause surface is now extremely tight:
- authored startup
ADD_SCENEis reached - but by the time
adsPlayChunk()returns, no running scene thread survives (numThreads == 0)
- authored startup
- so the bug is no longer “wrong chunk” or “startup
ADD_SCENEnever hit” - it is now in the immediate lifetime of those just-added startup threads
- this proves the first exact ACTIVITY chunk really is reaching the authored startup
- Cleanup:
- reverted the temporary
adsAddScene(1:12/1:13)proof patch after validation
- reverted the temporary
- Next target:
- debug why the just-added ACTIVITY startup threads do not survive past the first-chunk bootstrap
- the narrowest live candidates are:
- duplicate/running suppression
- immediate stop/termination in the same chunk
- zero/invalid TTM entrypoint after add
19:39 PDT - Late ACTIVITY background-thread suppression does not move the settled ocean
- Temporary proof:
- in
repo:/ads.c, suppressed only:islandAnimate(&ttmBackgroundThread)islandRedrawWave(&ttmBackgroundThread)
- only for exact
ACTIVITY.ADS 1aftergrGetCurrentFrame() >= 1000 - left all
ttmPlay(...)scene-thread playback intact
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-bgthread-suppress
- direct frame capture under:
- Result:
frame_04910stays byte-identical to the original settled ocean:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
frame_05000is also byte-identical to the same hash- full sampled-run concatenated PNG hash:
31354944eee50c711b70eecc308bb70028cd04247471e2646de8b0ad45fa5002
- Important conclusion:
- the late stable ACTIVITY ocean is not being sustained by the background thread
- neither late wave redraw nor late background animation is the repopulation path
- Cleanup:
- reverted the temporary late background-thread suppression proof patch
19:41 PDT - Late ttmPlay() suppression also does not move the settled ocean
- Temporary proof:
- in
repo:/ads.c, suppressed:ttmPlay(&ttmThreads[i])
- only for exact
ACTIVITY.ADS 1aftergrGetCurrentFrame() >= 1000 - left background-thread work intact
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-ttm-suppress
- direct frame capture under:
- Result:
frame_04910stays byte-identical to the original settled ocean:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
frame_05000is also identical- early lead-in still differs:
frame_04770 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- full sampled-run concatenated PNG hash is unchanged from the background-thread proof:
31354944eee50c711b70eecc308bb70028cd04247471e2646de8b0ad45fa5002
- Important conclusion:
- by the settled
04910+failure band, the stable bad ocean no longer depends on latettmPlay()execution either - so the image is surviving after both:
- late background-thread work
- late scene-thread opcode playback
- by the settled
- Cleanup:
- reverted the temporary late
ttmPlay()suppression proof patch
- reverted the temporary late
19:43 PDT - Zeroing all background tiles immediately before LoadImage() still does not move the settled ocean
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrDrawBackground():- zeroed all four
bgTile*->pixels - forced their dirty ranges full-height
- zeroed all four
- only for exact
ACTIVITY.ADS 1aftergrCurrentFrame >= 1000 - this is the last shared upload boundary before the frame reaches VRAM
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-upload-blackproof
- direct frame capture under:
- Result:
frame_04910still matches the exact original settled ocean hash:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
frame_05000also stays identical- early lead-in still differs:
frame_04770 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- full sampled-run concatenated PNG hash remains:
31354944eee50c711b70eecc308bb70028cd04247471e2646de8b0ad45fa5002
- Important conclusion:
- the settled ACTIVITY ocean is surviving even past the normal tile-upload boundary
- it is not explained by:
- late background-thread redraw
- late
ttmPlay()scene-thread execution - or even the current contents of the four background tile RAM surfaces at
grDrawBackground()upload time
- Cleanup:
- reverted the temporary upload-blackproof patch after validation
- Next target:
- move the proof surface to framebuffer/display ownership itself:
- determine whether the visible settled ocean is coming from a different renderer surface/path than the tile upload path
- move the proof surface to framebuffer/display ownership itself:
19:47 PDT - Direct late framebuffer black upload does change the settled ACTIVITY image
- Temporary proof:
- in
repo:/graphics_ps1.c, immediately aftergrDrawBackground()insidegrUpdateDisplay():- uploaded a static all-zero
320x240tile into all four framebuffer quadrants
- uploaded a static all-zero
- only for exact
ACTIVITY.ADS 1aftergrCurrentFrame >= 1000
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-framebuffer-blackproof
- direct frame capture under:
- Result:
- the settled late ocean disappears
frame_04910,frame_04770, andframe_05000all become:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- full sampled-run concatenated PNG hash changes to:
8d305707d22f695b403e105f5ca11807281cfb1799cb6074b4a0e96889767e56
- Important conclusion:
- the visible settled ACTIVITY image is absolutely on the normal displayed framebuffer
- so the earlier negative
grDrawBackground()tile-scrub proof does not mean “different renderer/output surface” - instead, it means the settled ocean is surviving into the visible framebuffer in a way that was not affected by the earlier tile-RAM/upload scrubs
- the likely next target is now the exact interaction between:
- dirty-row tracking / upload eligibility
- framebuffer persistence across frames
- any direct framebuffer writes outside the tile upload proof
- Cleanup:
- reverted the temporary framebuffer-blackproof patch after validation
- Next target:
- instrument why scrubbing the tile-upload source did not move the framebuffer, while a direct post-upload framebuffer write did
19:51 PDT - Replacing the grDrawBackground() upload source with black does change the settled ACTIVITY image
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrDrawBackground():- replaced the normal
LoadImage(..., tiles[i]->pixels + minY * w)source with a static all-zero upload buffer
- replaced the normal
- only for exact
ACTIVITY.ADS 1aftergrCurrentFrame >= 1000
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-upload-source-blackproof
- direct frame capture under:
- Result:
- the settled late ocean disappears
frame_04910,frame_04770, andframe_05000all become:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- full sampled-run concatenated PNG hash changes to:
8d305707d22f695b403e105f5ca11807281cfb1799cb6074b4a0e96889767e56
- Important conclusion:
- the settled ACTIVITY image is still sourced through the normal
grDrawBackground()upload path - so the earlier negative “tile memset + mark dirty” proof was not evidence of an alternate renderer/output surface
- it only means that forcing the tile RAM contents to zero in that earlier proof did not actually alter the uploaded source in the way expected
- the settled ACTIVITY image is still sourced through the normal
- Cleanup:
- reverted the temporary upload-source-blackproof patch after validation
- Next target:
- debug why the earlier tile-RAM scrub failed while replacing the actual
LoadImagesource succeeded - most likely surfaces now:
- dirty-row/source-pointer semantics in
grDrawBackground() - tile RAM contents vs. expected upload window
- dirty-row/source-pointer semantics in
- debug why the earlier tile-RAM scrub failed while replacing the actual
19:56 PDT - Overwriting the exact heap upload slice still does not move the settled ocean
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrDrawBackground():- for exact
ACTIVITY.ADS 1, overwrote the exact heap slicetiles[i]->pixels + minY * w
- with zeroes immediately before the normal
LoadImage(...)call
- for exact
- this kept:
- the same rect
- the same source pointer
- the same upload path
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-upload-slice-blackproof
- direct frame capture under:
- Result:
frame_04910still matches the original settled ocean:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
frame_05000also stays identical- earlier lead-in still differs:
frame_04770 = 0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- full sampled-run concatenated PNG hash remains the old one:
31354944eee50c711b70eecc308bb70028cd04247471e2646de8b0ad45fa5002
- Important conclusion:
- replacing the
LoadImagesource pointer works - but mutating the exact heap source slice in place does not
- so the remaining renderer bug is now strongly pointing at source-memory behavior, not upload-path selection
- replacing the
- Cleanup:
- reverted the temporary upload-slice-blackproof patch after validation
20:00 PDT - Fresh-address upload only perturbs the lead-in, not the settled 04910 ocean
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrDrawBackground():- for exact
ACTIVITY.ADS 1only whengrCurrentFramewas in the tight4909..4910band - replaced the normal source with a freshly allocated zeroed upload buffer
- for exact
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-upload-freshaddr-4910
- direct frame capture under:
- Result:
frame_04910still matches the original settled ocean:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
frame_05000also stays identical- but the immediate pre-failure frame does change:
frame_04900 = d9ee0d2e8f8465ea471c9c79223f1177c7225a238274f7f68ab32ed94312c0a0
- Important conclusion:
- this is not clean enough to claim the “fresh unique source address fixes late uploads” theory
- but it does show the renderer is sensitive to source-buffer identity/timing right at the failure boundary
- Cleanup:
- reverted the temporary upload-freshaddr-4910 patch after validation
- Next target:
- keep the bug class as renderer-side
LoadImagesource-memory behavior - but move the proof surface to a slightly wider
04900–04910upload window, since the exact-frame gate is too narrow to be definitive
- keep the bug class as renderer-side
20:01 PDT - Widened fresh-address upload window is cleanly negative
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrDrawBackground():- for exact
ACTIVITY.ADS 1, replaced the normal upload source with a freshly allocated zeroed buffer - across the full
grCurrentFramewindow4900..4910
- for exact
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-upload-freshaddr-4900-4910
- direct frame capture under:
- Result:
frame_04900is unchanged:d9ee0d2e8f8465ea471c9c79223f1177c7225a238274f7f68ab32ed94312c0a0
frame_04910is unchanged:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
frame_05000is unchanged:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- full sampled-run concatenated PNG hash also stays unchanged:
31354944eee50c711b70eecc308bb70028cd04247471e2646de8b0ad45fa5002
- Important conclusion:
- the simple “fresh unique source address in the failure band fixes the late renderer state” theory is now ruled out
- current renderer-side read is narrower:
- replacing the upload source globally with a static black buffer works
- mutating the in-place heap slice does not
- swapping to fresh zeroed buffers only in the handoff band also does not
- Cleanup:
- reverted the widened fresh-address proof patch after validation
- Next target:
- stop treating this as a generic source-address problem
- move to exact upload-window geometry / dirty-range behavior at
04910
20:05 PDT - Forcing full-height uploads across 04900–04910 makes the settled ocean disappear
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrDrawBackground():- for exact
ACTIVITY.ADS 1and only duringgrCurrentFrame4900..4910 - forced every background tile upload window to full height:
minY = 0maxY = tile->height - 1
- and replaced the upload source with a static all-zero black buffer
- for exact
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-force-dirty-black-4900-4910
- direct frame capture under:
- Result:
frame_04900changes to black:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
frame_04910changes to black:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
frame_05000also stays black:0092914af727e5d656269735cf9d7cfffa0cb120f2966b57c23fa9e0fe1095e9
- full sampled-run concatenated PNG hash:
8d305707d22f695b403e105f5ca11807281cfb1799cb6074b4a0e96889767e56
- Important conclusion:
- this is the sharpest renderer cut so far
- the settled ACTIVITY ocean is still entirely controlled by
grDrawBackground()uploads - and the key remaining bug class is now dirty-range/upload eligibility at the handoff window
- i.e. the normal upload window at
04910is not covering or replacing the visible framebuffer the way it should
- Cleanup:
- reverted the temporary force-dirty-black proof patch after validation
- Next target:
- inspect the exact
minY/maxYdirty-row state leading into04910 - determine why the normal dirty window is insufficient while forced full-height upload fixes the frame
- inspect the exact
20:10 PDT - Forcing full-height uploads with the normal source does nothing
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrDrawBackground():- for exact
ACTIVITY.ADS 1duringgrCurrentFrame4900..4910 - forced:
minY = 0maxY = tile->height - 1
- but kept the normal upload source:
tiles[i]->pixels + minY * w
- for exact
- in
- Validation run:
- direct frame capture under:
repo:/regtest-results/activity1-ads1-seed1-force-dirty-normal-4900-4910
- direct frame capture under:
- Result:
frame_04900is unchanged:d9ee0d2e8f8465ea471c9c79223f1177c7225a238274f7f68ab32ed94312c0a0
frame_04910is unchanged:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
frame_05000is unchanged:59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- full sampled-run concatenated PNG hash also remains unchanged:
31354944eee50c711b70eecc308bb70028cd04247471e2646de8b0ad45fa5002
- Important conclusion:
- this cleanly rules out “dirty upload window is too small” as a sufficient explanation by itself
- widening the upload window alone does not move the settled ocean
- the current renderer-side read is now:
- replacing the upload source with black works
- widening the upload window alone does not
- so the decisive remaining issue is the content of the normal upload source at the handoff, not just row eligibility
- Cleanup:
- reverted the temporary force-dirty-normal proof patch after validation
- Next target:
- inspect what is actually in the normal tile upload source at
04910 - and why it remains ocean while black-source substitution succeeds
- inspect what is actually in the normal tile upload source at
18:55 PDT - Returning from islandInit() immediately after grLoadScreen(...) reproduces the exact original ocean hash
- Temporary proof:
- in
repo:/island.c, for exactACTIVITY.ADS 1boot path:- kept the normal initial:
grLoadScreen("NIGHT.SCR")orgrLoadScreen("OCEAN0?.SCR")
- then returned immediately from
islandInit()
- kept the normal initial:
- this bypassed all later island composition:
- raft
BACKGRND.BMP- clouds
- island sprites
- initial wave draws
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-island-loadonly/result.json
- Result:
- run exits cleanly with the exact original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the exact original stable bad-ocean hash:
- Important conclusion:
- later island composition is not required at all for the persistent ACTIVITY failure
- the stable bad ocean is already fully established by the initial
grLoadScreen(...)path insideislandInit() - this collapses the remaining causal surface tightly onto:
adsInitIsland()islandInit()- specifically the initial
OCEAN0?.SCR/NIGHT.SCRbackground load and the state path that leads there
- Cleanup:
- reverted the temporary early return after
grLoadScreen(...)
- reverted the temporary early return after
19:00 PDT - Scrubbing to black immediately after exact adsPlay(ACTIVITY.ADS, 1) no-launch return does nothing
- Temporary proof:
- in
repo:/story.c, immediately after:adsPlay(scene->adsName, scene->adsTagNo)
- for exact
ACTIVITY.ADS 1, if:!ps1AdsLastPlayLaunched
- then called:
adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-post-adsplay-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the stable ACTIVITY ocean path is not being explained by the exact post-
adsPlay(ACTIVITY.ADS, 1)no-launch return hook - either:
- that exact playback hook is not reached on the stable failure path
- or the late ocean path is being re-established elsewhere after it
- combined with the exact prepare proofs, this keeps the strongest remaining live suspect centered on the prepare/intro-to-scene handoff rather than the exact post-playback failure branch
- the stable ACTIVITY ocean path is not being explained by the exact post-
- Cleanup:
- reverted the temporary post-
adsPlay()adsNoIsland()proof patch
- reverted the temporary post-
19:05 PDT - Scrubbing to black immediately before storyPlayPreparedScene(finalScene, ...) also does nothing
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- called:
adsNoIsland()
- immediately before:
storyPlayPreparedScene(finalScene, prevSpot, prevHdg)
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-pre-playprepared-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
storyPlayPreparedScene(...)handoff hook instoryPlay() - that makes the current failure shape more explicit:
- exact ACTIVITY prepare has strong causal linkage to the ocean
- but the stable bad path is not behaving like the straightforward top-level exact-playback branch we expected
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
- Cleanup:
- reverted the temporary pre-
storyPlayPreparedScene()adsNoIsland()proof patch
- reverted the temporary pre-
19:10 PDT - Forcing the obvious bootScene == NULL fallback prepare path to black also does nothing
- Temporary proof:
- in
repo:/story.c, beforestoryPrepareSceneState(finalScene):- if
bootScene == NULL - and exact
ACTIVITY.ADS 1override was still pending - called
adsNoIsland()instead ofstoryPrepareSceneState(finalScene)
- if
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-fallback-bootscene-null/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ocean is not being explained by the obvious
bootScene == NULLfallback prepare branch either - so the remaining bad path is more specific than:
- exact top-level playprepared branch
- exact post-adsPlay no-launch branch
- obvious
bootScene == NULLfallback prepare
- the settled ocean is not being explained by the obvious
- Cleanup:
- reverted the temporary fallback proof patch
19:16 PDT - Even adsReleaseIsland(); adsNoIsland(); before exact playback leaves the original ocean hash untouched
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- immediately before
storyPlayPreparedScene(...), called:adsReleaseIsland()adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-release-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- this is not just island/background state persisting because
adsNoIsland()is too weak after prepare - even explicit island release plus black-background init at that top-level handoff point does not move the result
- that strengthens the read that the stable bad-ocean path is bypassing or outliving the obvious top-level exact handoff hooks entirely
- this is not just island/background state persisting because
- Cleanup:
- reverted the temporary
adsReleaseIsland(); adsNoIsland();proof patch
- reverted the temporary
19:21 PDT - Forcing late grLoadScreen(\"OCEAN0?.SCR\") loads to black also does nothing
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrLoadScreen(char *strArg):- if exact
ACTIVITY.ADS 1override is pending - and
grGetCurrentFrame() >= 1000 - and
strArgmatchesOCEAN0?.SCR - then call
grInitEmptyBackground()and return
- if exact
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-late-ocean-screen-blackproof/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled
04910+ocean is not being actively re-established by a lategrLoadScreen(\"OCEAN0?.SCR\")call on the failure path - that strengthens the read that the bad ocean is coming from an earlier background load that survives into the late window, not a later screen reload
- the settled
- Cleanup:
- reverted the temporary late-
grLoadScreen()blackproof patch
- reverted the temporary late-
19:26 PDT - Zeroing restored background tiles every frame still leaves the exact original ocean hash
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrRestoreBgTiles():- after restoring clean copies
- if exact
ACTIVITY.ADS 1override is pending - and
grCurrentFrame >= 1000 - memset the active restored background tiles to black
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-restore-blackproof/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ocean is being repopulated after
grRestoreBgTiles(), before upload - so it is not merely a stale clean-background restore artifact surviving untouched through the late window
- this moves the remaining live suspect surface to late per-frame compositing/drawing, not background restore
- the settled ocean is being repopulated after
- Cleanup:
- reverted the temporary restore-blackproof patch
19:30 PDT - Disabling saved-rect replay still leaves the exact original ocean hash
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrApplySavedRects():- if exact
ACTIVITY.ADS 1override is pending - and
grCurrentFrame >= 1000 - return immediately without replaying any saved rects
- if exact
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-savedrect-blackproof/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- saved-rect replay is not the repopulation path either
- the settled ocean is still being drawn later in the frame by live compositing/drawing after:
grRestoreBgTiles()- and after
grApplySavedRects()
- Cleanup:
- reverted the temporary saved-rect blackproof patch
- Next target:
- move directly into late live compositing between restore and upload:
ttmPlay()/ background-thread draws / holiday-thread draws / sprite compositing
- move directly into late live compositing between restore and upload:
18:55 PDT - Returning from islandInit() immediately after grLoadScreen(...) reproduces the exact original ocean hash
- Temporary proof:
- in
repo:/island.c, for exactACTIVITY.ADS 1boot path:- kept the normal initial:
grLoadScreen("NIGHT.SCR")orgrLoadScreen("OCEAN0?.SCR")
- then returned immediately from
islandInit()
- kept the normal initial:
- this bypassed all later island composition:
- raft
BACKGRND.BMP- clouds
- island sprites
- initial wave draws
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-island-loadonly/result.json
- Result:
- run exits cleanly with the exact original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the exact original stable bad-ocean hash:
- Important conclusion:
- later island composition is not required at all for the persistent ACTIVITY failure
- the stable bad ocean is already fully established by the initial
grLoadScreen(...)path insideislandInit() - this collapses the remaining causal surface tightly onto:
adsInitIsland()islandInit()- specifically the initial
OCEAN0?.SCR/NIGHT.SCRbackground load and the state path that leads there
- Cleanup:
- reverted the temporary early return after
grLoadScreen(...)
- reverted the temporary early return after
19:00 PDT - Scrubbing to black immediately after exact adsPlay(ACTIVITY.ADS, 1) no-launch return does nothing
- Temporary proof:
- in
repo:/story.c, immediately after:adsPlay(scene->adsName, scene->adsTagNo)
- for exact
ACTIVITY.ADS 1, if:!ps1AdsLastPlayLaunched
- then called:
adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-post-adsplay-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the stable ACTIVITY ocean path is not being explained by the exact post-
adsPlay(ACTIVITY.ADS, 1)no-launch return hook - either:
- that exact playback hook is not reached on the stable failure path
- or the late ocean path is being re-established elsewhere after it
- combined with the exact prepare proofs, this keeps the strongest remaining live suspect centered on the prepare/intro-to-scene handoff rather than the exact post-playback failure branch
- the stable ACTIVITY ocean path is not being explained by the exact post-
- Cleanup:
- reverted the temporary post-
adsPlay()adsNoIsland()proof patch
- reverted the temporary post-
19:05 PDT - Scrubbing to black immediately before storyPlayPreparedScene(finalScene, ...) also does nothing
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- called:
adsNoIsland()
- immediately before:
storyPlayPreparedScene(finalScene, prevSpot, prevHdg)
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-pre-playprepared-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
storyPlayPreparedScene(...)handoff hook instoryPlay() - that makes the current failure shape more explicit:
- exact ACTIVITY prepare has strong causal linkage to the ocean
- but the stable bad path is not behaving like the straightforward top-level exact-playback branch we expected
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
- Cleanup:
- reverted the temporary pre-
storyPlayPreparedScene()adsNoIsland()proof patch
- reverted the temporary pre-
19:10 PDT - Forcing the obvious bootScene == NULL fallback prepare path to black also does nothing
- Temporary proof:
- in
repo:/story.c, beforestoryPrepareSceneState(finalScene):- if
bootScene == NULL - and exact
ACTIVITY.ADS 1override was still pending - called
adsNoIsland()instead ofstoryPrepareSceneState(finalScene)
- if
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-fallback-bootscene-null/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ocean is not being explained by the obvious
bootScene == NULLfallback prepare branch either - so the remaining bad path is more specific than:
- exact top-level playprepared branch
- exact post-adsPlay no-launch branch
- obvious
bootScene == NULLfallback prepare
- the settled ocean is not being explained by the obvious
- Cleanup:
- reverted the temporary fallback proof patch
19:16 PDT - Even adsReleaseIsland(); adsNoIsland(); before exact playback leaves the original ocean hash untouched
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- immediately before
storyPlayPreparedScene(...), called:adsReleaseIsland()adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-release-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- this is not just island/background state persisting because
adsNoIsland()is too weak after prepare - even explicit island release plus black-background init at that top-level handoff point does not move the result
- that strengthens the read that the stable bad-ocean path is bypassing or outliving the obvious top-level exact handoff hooks entirely
- this is not just island/background state persisting because
- Cleanup:
- reverted the temporary
adsReleaseIsland(); adsNoIsland();proof patch
- reverted the temporary
19:21 PDT - Forcing late grLoadScreen(\"OCEAN0?.SCR\") loads to black also does nothing
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrLoadScreen(char *strArg):- if exact
ACTIVITY.ADS 1override is pending - and
grGetCurrentFrame() >= 1000 - and
strArgmatchesOCEAN0?.SCR - then call
grInitEmptyBackground()and return
- if exact
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-late-ocean-screen-blackproof/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled
04910+ocean is not being actively re-established by a lategrLoadScreen(\"OCEAN0?.SCR\")call on the failure path - that strengthens the read that the bad ocean is coming from an earlier background load that survives into the late window, not a later screen reload
- the settled
- Cleanup:
- reverted the temporary late-
grLoadScreen()blackproof patch
- reverted the temporary late-
19:26 PDT - Zeroing restored background tiles every frame still leaves the exact original ocean hash
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrRestoreBgTiles():- after restoring clean copies
- if exact
ACTIVITY.ADS 1override is pending - and
grCurrentFrame >= 1000 - memset the active restored background tiles to black
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-restore-blackproof/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ocean is being repopulated after
grRestoreBgTiles(), before upload - so it is not merely a stale clean-background restore artifact surviving untouched through the late window
- this moves the remaining live suspect surface to late per-frame compositing/drawing, not background restore
- the settled ocean is being repopulated after
- Cleanup:
- reverted the temporary restore-blackproof patch
- Next target:
- instrument or suppress late per-frame compositing paths between
grRestoreBgTiles()andgrDrawBackground()to identify what is repopulating the ocean after restore
- instrument or suppress late per-frame compositing paths between
18:55 PDT - Returning from islandInit() immediately after grLoadScreen(...) reproduces the exact original ocean hash
- Temporary proof:
- in
repo:/island.c, for exactACTIVITY.ADS 1boot path:- kept the normal initial:
grLoadScreen("NIGHT.SCR")orgrLoadScreen("OCEAN0?.SCR")
- then returned immediately from
islandInit()
- kept the normal initial:
- this bypassed all later island composition:
- raft
BACKGRND.BMP- clouds
- island sprites
- initial wave draws
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-island-loadonly/result.json
- Result:
- run exits cleanly with the exact original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the exact original stable bad-ocean hash:
- Important conclusion:
- later island composition is not required at all for the persistent ACTIVITY failure
- the stable bad ocean is already fully established by the initial
grLoadScreen(...)path insideislandInit() - this collapses the remaining causal surface tightly onto:
adsInitIsland()islandInit()- specifically the initial
OCEAN0?.SCR/NIGHT.SCRbackground load and the state path that leads there
- Cleanup:
- reverted the temporary early return after
grLoadScreen(...)
- reverted the temporary early return after
19:00 PDT - Scrubbing to black immediately after exact adsPlay(ACTIVITY.ADS, 1) no-launch return does nothing
- Temporary proof:
- in
repo:/story.c, immediately after:adsPlay(scene->adsName, scene->adsTagNo)
- for exact
ACTIVITY.ADS 1, if:!ps1AdsLastPlayLaunched
- then called:
adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-post-adsplay-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the stable ACTIVITY ocean path is not being explained by the exact post-
adsPlay(ACTIVITY.ADS, 1)no-launch return hook - either:
- that exact playback hook is not reached on the stable failure path
- or the late ocean path is being re-established elsewhere after it
- combined with the exact prepare proofs, this keeps the strongest remaining live suspect centered on the prepare/intro-to-scene handoff rather than the exact post-playback failure branch
- the stable ACTIVITY ocean path is not being explained by the exact post-
- Cleanup:
- reverted the temporary post-
adsPlay()adsNoIsland()proof patch
- reverted the temporary post-
19:05 PDT - Scrubbing to black immediately before storyPlayPreparedScene(finalScene, ...) also does nothing
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- called:
adsNoIsland()
- immediately before:
storyPlayPreparedScene(finalScene, prevSpot, prevHdg)
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-pre-playprepared-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
storyPlayPreparedScene(...)handoff hook instoryPlay() - that makes the current failure shape more explicit:
- exact ACTIVITY prepare has strong causal linkage to the ocean
- but the stable bad path is not behaving like the straightforward top-level exact-playback branch we expected
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
- Cleanup:
- reverted the temporary pre-
storyPlayPreparedScene()adsNoIsland()proof patch
- reverted the temporary pre-
19:10 PDT - Forcing the obvious bootScene == NULL fallback prepare path to black also does nothing
- Temporary proof:
- in
repo:/story.c, beforestoryPrepareSceneState(finalScene):- if
bootScene == NULL - and exact
ACTIVITY.ADS 1override was still pending - called
adsNoIsland()instead ofstoryPrepareSceneState(finalScene)
- if
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-fallback-bootscene-null/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ocean is not being explained by the obvious
bootScene == NULLfallback prepare branch either - so the remaining bad path is more specific than:
- exact top-level playprepared branch
- exact post-adsPlay no-launch branch
- obvious
bootScene == NULLfallback prepare
- the settled ocean is not being explained by the obvious
- Cleanup:
- reverted the temporary fallback proof patch
19:16 PDT - Even adsReleaseIsland(); adsNoIsland(); before exact playback leaves the original ocean hash untouched
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- immediately before
storyPlayPreparedScene(...), called:adsReleaseIsland()adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-release-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- this is not just island/background state persisting because
adsNoIsland()is too weak after prepare - even explicit island release plus black-background init at that top-level handoff point does not move the result
- that strengthens the read that the stable bad-ocean path is bypassing or outliving the obvious top-level exact handoff hooks entirely
- this is not just island/background state persisting because
- Cleanup:
- reverted the temporary
adsReleaseIsland(); adsNoIsland();proof patch
- reverted the temporary
19:21 PDT - Forcing late grLoadScreen(\"OCEAN0?.SCR\") loads to black also does nothing
- Temporary proof:
- in
repo:/graphics_ps1.c, insidegrLoadScreen(char *strArg):- if exact
ACTIVITY.ADS 1override is pending - and
grGetCurrentFrame() >= 1000 - and
strArgmatchesOCEAN0?.SCR - then call
grInitEmptyBackground()and return
- if exact
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-late-ocean-screen-blackproof/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled
04910+ocean is not being actively re-established by a lategrLoadScreen(\"OCEAN0?.SCR\")call on the failure path - that strengthens the read that the bad ocean is coming from an earlier background load that survives into the late window, not a later screen reload
- the settled
- Cleanup:
- reverted the temporary late-
grLoadScreen()blackproof patch
- reverted the temporary late-
- Next target:
- move the proof surface to background persistence and framebuffer ownership itself, because both:
- obvious story-level branches
- late renderer reload hypothesis
- are now ruled out directly
- move the proof surface to background persistence and framebuffer ownership itself, because both:
18:55 PDT - Returning from islandInit() immediately after grLoadScreen(...) reproduces the exact original ocean hash
- Temporary proof:
- in
repo:/island.c, for exactACTIVITY.ADS 1boot path:- kept the normal initial:
grLoadScreen("NIGHT.SCR")orgrLoadScreen("OCEAN0?.SCR")
- then returned immediately from
islandInit()
- kept the normal initial:
- this bypassed all later island composition:
- raft
BACKGRND.BMP- clouds
- island sprites
- initial wave draws
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-island-loadonly/result.json
- Result:
- run exits cleanly with the exact original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the exact original stable bad-ocean hash:
- Important conclusion:
- later island composition is not required at all for the persistent ACTIVITY failure
- the stable bad ocean is already fully established by the initial
grLoadScreen(...)path insideislandInit() - this collapses the remaining causal surface tightly onto:
adsInitIsland()islandInit()- specifically the initial
OCEAN0?.SCR/NIGHT.SCRbackground load and the state path that leads there
- Cleanup:
- reverted the temporary early return after
grLoadScreen(...)
- reverted the temporary early return after
19:00 PDT - Scrubbing to black immediately after exact adsPlay(ACTIVITY.ADS, 1) no-launch return does nothing
- Temporary proof:
- in
repo:/story.c, immediately after:adsPlay(scene->adsName, scene->adsTagNo)
- for exact
ACTIVITY.ADS 1, if:!ps1AdsLastPlayLaunched
- then called:
adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-post-adsplay-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the stable ACTIVITY ocean path is not being explained by the exact post-
adsPlay(ACTIVITY.ADS, 1)no-launch return hook - either:
- that exact playback hook is not reached on the stable failure path
- or the late ocean path is being re-established elsewhere after it
- combined with the exact prepare proofs, this keeps the strongest remaining live suspect centered on the prepare/intro-to-scene handoff rather than the exact post-playback failure branch
- the stable ACTIVITY ocean path is not being explained by the exact post-
- Cleanup:
- reverted the temporary post-
adsPlay()adsNoIsland()proof patch
- reverted the temporary post-
19:05 PDT - Scrubbing to black immediately before storyPlayPreparedScene(finalScene, ...) also does nothing
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- called:
adsNoIsland()
- immediately before:
storyPlayPreparedScene(finalScene, prevSpot, prevHdg)
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-pre-playprepared-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
storyPlayPreparedScene(...)handoff hook instoryPlay() - that makes the current failure shape more explicit:
- exact ACTIVITY prepare has strong causal linkage to the ocean
- but the stable bad path is not behaving like the straightforward top-level exact-playback branch we expected
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
- Cleanup:
- reverted the temporary pre-
storyPlayPreparedScene()adsNoIsland()proof patch
- reverted the temporary pre-
19:10 PDT - Forcing the obvious bootScene == NULL fallback prepare path to black also does nothing
- Temporary proof:
- in
repo:/story.c, beforestoryPrepareSceneState(finalScene):- if
bootScene == NULL - and exact
ACTIVITY.ADS 1override was still pending - called
adsNoIsland()instead ofstoryPrepareSceneState(finalScene)
- if
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-fallback-bootscene-null/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ocean is not being explained by the obvious
bootScene == NULLfallback prepare branch either - so the remaining bad path is more specific than:
- exact top-level playprepared branch
- exact post-adsPlay no-launch branch
- obvious
bootScene == NULLfallback prepare
- the settled ocean is not being explained by the obvious
- Cleanup:
- reverted the temporary fallback proof patch
19:16 PDT - Even adsReleaseIsland(); adsNoIsland(); before exact playback leaves the original ocean hash untouched
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- immediately before
storyPlayPreparedScene(...), called:adsReleaseIsland()adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-release-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- this is not just island/background state persisting because
adsNoIsland()is too weak after prepare - even explicit island release plus black-background init at that top-level handoff point does not move the result
- that strengthens the read that the stable bad-ocean path is bypassing or outliving the obvious top-level exact handoff hooks entirely
- this is not just island/background state persisting because
- Cleanup:
- reverted the temporary
adsReleaseIsland(); adsNoIsland();proof patch
- reverted the temporary
- Next target:
- switch from behavioral scrubs to a more direct runtime identity proof for the code path active at the
04910settled-ocean window, because the obvious top-level hooks and fallback branches are now directly ruled out
- switch from behavioral scrubs to a more direct runtime identity proof for the code path active at the
18:55 PDT - Returning from islandInit() immediately after grLoadScreen(...) reproduces the exact original ocean hash
- Temporary proof:
- in
repo:/island.c, for exactACTIVITY.ADS 1boot path:- kept the normal initial:
grLoadScreen("NIGHT.SCR")orgrLoadScreen("OCEAN0?.SCR")
- then returned immediately from
islandInit()
- kept the normal initial:
- this bypassed all later island composition:
- raft
BACKGRND.BMP- clouds
- island sprites
- initial wave draws
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-island-loadonly/result.json
- Result:
- run exits cleanly with the exact original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the exact original stable bad-ocean hash:
- Important conclusion:
- later island composition is not required at all for the persistent ACTIVITY failure
- the stable bad ocean is already fully established by the initial
grLoadScreen(...)path insideislandInit() - this collapses the remaining causal surface tightly onto:
adsInitIsland()islandInit()- specifically the initial
OCEAN0?.SCR/NIGHT.SCRbackground load and the state path that leads there
- Cleanup:
- reverted the temporary early return after
grLoadScreen(...)
- reverted the temporary early return after
19:00 PDT - Scrubbing to black immediately after exact adsPlay(ACTIVITY.ADS, 1) no-launch return does nothing
- Temporary proof:
- in
repo:/story.c, immediately after:adsPlay(scene->adsName, scene->adsTagNo)
- for exact
ACTIVITY.ADS 1, if:!ps1AdsLastPlayLaunched
- then called:
adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-post-adsplay-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the stable ACTIVITY ocean path is not being explained by the exact post-
adsPlay(ACTIVITY.ADS, 1)no-launch return hook - either:
- that exact playback hook is not reached on the stable failure path
- or the late ocean path is being re-established elsewhere after it
- combined with the exact prepare proofs, this keeps the strongest remaining live suspect centered on the prepare/intro-to-scene handoff rather than the exact post-playback failure branch
- the stable ACTIVITY ocean path is not being explained by the exact post-
- Cleanup:
- reverted the temporary post-
adsPlay()adsNoIsland()proof patch
- reverted the temporary post-
19:05 PDT - Scrubbing to black immediately before storyPlayPreparedScene(finalScene, ...) also does nothing
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- called:
adsNoIsland()
- immediately before:
storyPlayPreparedScene(finalScene, prevSpot, prevHdg)
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-pre-playprepared-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
storyPlayPreparedScene(...)handoff hook instoryPlay() - that makes the current failure shape more explicit:
- exact ACTIVITY prepare has strong causal linkage to the ocean
- but the stable bad path is not behaving like the straightforward top-level exact-playback branch we expected
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
- Cleanup:
- reverted the temporary pre-
storyPlayPreparedScene()adsNoIsland()proof patch
- reverted the temporary pre-
19:10 PDT - Forcing the obvious bootScene == NULL fallback prepare path to black also does nothing
- Temporary proof:
- in
repo:/story.c, beforestoryPrepareSceneState(finalScene):- if
bootScene == NULL - and exact
ACTIVITY.ADS 1override was still pending - called
adsNoIsland()instead ofstoryPrepareSceneState(finalScene)
- if
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-fallback-bootscene-null/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ocean is not being explained by the obvious
bootScene == NULLfallback prepare branch either - so the remaining bad path is more specific than:
- exact top-level playprepared branch
- exact post-adsPlay no-launch branch
- obvious
bootScene == NULLfallback prepare
- the settled ocean is not being explained by the obvious
- Cleanup:
- reverted the temporary fallback proof patch
- Next target:
- move to a more direct runtime identity proof surface for the actual branch executing at the
04910settled-ocean window, because the obvious story-level branches are now mostly ruled out by direct behavioral proofs
- move to a more direct runtime identity proof surface for the actual branch executing at the
18:55 PDT - Returning from islandInit() immediately after grLoadScreen(...) reproduces the exact original ocean hash
- Temporary proof:
- in
repo:/island.c, for exactACTIVITY.ADS 1boot path:- kept the normal initial:
grLoadScreen("NIGHT.SCR")orgrLoadScreen("OCEAN0?.SCR")
- then returned immediately from
islandInit()
- kept the normal initial:
- this bypassed all later island composition:
- raft
BACKGRND.BMP- clouds
- island sprites
- initial wave draws
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-island-loadonly/result.json
- Result:
- run exits cleanly with the exact original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the exact original stable bad-ocean hash:
- Important conclusion:
- later island composition is not required at all for the persistent ACTIVITY failure
- the stable bad ocean is already fully established by the initial
grLoadScreen(...)path insideislandInit() - this collapses the remaining causal surface tightly onto:
adsInitIsland()islandInit()- specifically the initial
OCEAN0?.SCR/NIGHT.SCRbackground load and the state path that leads there
- Cleanup:
- reverted the temporary early return after
grLoadScreen(...)
- reverted the temporary early return after
19:00 PDT - Scrubbing to black immediately after exact adsPlay(ACTIVITY.ADS, 1) no-launch return does nothing
- Temporary proof:
- in
repo:/story.c, immediately after:adsPlay(scene->adsName, scene->adsTagNo)
- for exact
ACTIVITY.ADS 1, if:!ps1AdsLastPlayLaunched
- then called:
adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-post-adsplay-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the stable ACTIVITY ocean path is not being explained by the exact post-
adsPlay(ACTIVITY.ADS, 1)no-launch return hook - either:
- that exact playback hook is not reached on the stable failure path
- or the late ocean path is being re-established elsewhere after it
- combined with the exact prepare proofs, this keeps the strongest remaining live suspect centered on the prepare/intro-to-scene handoff rather than the exact post-playback failure branch
- the stable ACTIVITY ocean path is not being explained by the exact post-
- Cleanup:
- reverted the temporary post-
adsPlay()adsNoIsland()proof patch
- reverted the temporary post-
19:05 PDT - Scrubbing to black immediately before storyPlayPreparedScene(finalScene, ...) also does nothing
- Temporary proof:
- in
repo:/story.c, for:bootScene != NULL- exact
finalScene == ACTIVITY.ADS 1
- called:
adsNoIsland()
- immediately before:
storyPlayPreparedScene(finalScene, prevSpot, prevHdg)
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-pre-playprepared-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
storyPlayPreparedScene(...)handoff hook instoryPlay() - that makes the current failure shape more explicit:
- exact ACTIVITY prepare has strong causal linkage to the ocean
- but the stable bad path is not behaving like the straightforward top-level exact-playback branch we expected
- the settled ACTIVITY ocean path is bypassing or outliving even the top-level exact pre-
- Cleanup:
- reverted the temporary pre-
storyPlayPreparedScene()adsNoIsland()proof patch
- reverted the temporary pre-
- Next target:
- identify the alternate path or retry/re-entry path that preserves or re-establishes the prepared ocean while bypassing both:
- exact pre-
storyPlayPreparedScene(...) - exact post-
adsPlay(ACTIVITY.ADS, 1)no-launch cleanup
- exact pre-
- identify the alternate path or retry/re-entry path that preserves or re-establishes the prepared ocean while bypassing both:
18:55 PDT - Returning from islandInit() immediately after grLoadScreen(...) reproduces the exact original ocean hash
- Temporary proof:
- in
repo:/island.c, for exactACTIVITY.ADS 1boot path:- kept the normal initial:
grLoadScreen("NIGHT.SCR")orgrLoadScreen("OCEAN0?.SCR")
- then returned immediately from
islandInit()
- kept the normal initial:
- this bypassed all later island composition:
- raft
BACKGRND.BMP- clouds
- island sprites
- initial wave draws
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-island-loadonly/result.json
- Result:
- run exits cleanly with the exact original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the exact original stable bad-ocean hash:
- Important conclusion:
- later island composition is not required at all for the persistent ACTIVITY failure
- the stable bad ocean is already fully established by the initial
grLoadScreen(...)path insideislandInit() - this collapses the remaining causal surface tightly onto:
adsInitIsland()islandInit()- specifically the initial
OCEAN0?.SCR/NIGHT.SCRbackground load and the state path that leads there
- Cleanup:
- reverted the temporary early return after
grLoadScreen(...)
- reverted the temporary early return after
19:00 PDT - Scrubbing to black immediately after exact adsPlay(ACTIVITY.ADS, 1) no-launch return does nothing
- Temporary proof:
- in
repo:/story.c, immediately after:adsPlay(scene->adsName, scene->adsTagNo)
- for exact
ACTIVITY.ADS 1, if:!ps1AdsLastPlayLaunched
- then called:
adsNoIsland()
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-post-adsplay-noisland/result.json
- Result:
- run exits cleanly with the unchanged original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the unchanged original stable bad-ocean hash:
- Important conclusion:
- the stable ACTIVITY ocean path is not being explained by the exact post-
adsPlay(ACTIVITY.ADS, 1)no-launch return hook - either:
- that exact playback hook is not reached on the stable failure path
- or the late ocean path is being re-established elsewhere after it
- combined with the exact prepare proofs, this keeps the strongest remaining live suspect centered on the prepare/intro-to-scene handoff rather than the exact post-playback failure branch
- the stable ACTIVITY ocean path is not being explained by the exact post-
- Cleanup:
- reverted the temporary post-
adsPlay()adsNoIsland()proof patch
- reverted the temporary post-
- Next target:
- identify the path that reaches the settled ocean after exact ACTIVITY prepare while bypassing the exact post-
adsPlay(ACTIVITY.ADS, 1)no-launch cleanup hook
- identify the path that reaches the settled ocean after exact ACTIVITY prepare while bypassing the exact post-
18:55 PDT - Returning from islandInit() immediately after grLoadScreen(...) reproduces the exact original ocean hash
- Temporary proof:
- in
repo:/island.c, for exactACTIVITY.ADS 1boot path:- kept the normal initial:
grLoadScreen("NIGHT.SCR")orgrLoadScreen("OCEAN0?.SCR")
- then returned immediately from
islandInit()
- kept the normal initial:
- this bypassed all later island composition:
- raft
BACKGRND.BMP- clouds
- island sprites
- initial wave draws
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-island-loadonly/result.json
- Result:
- run exits cleanly with the exact original stable bad-ocean hash:
state_hash = 59cd749fc0952552db0f9748ec7421df288292773be8ee82f008bfb3f2fbf6fc
- run exits cleanly with the exact original stable bad-ocean hash:
- Important conclusion:
- later island composition is not required at all for the persistent ACTIVITY failure
- the stable bad ocean is already fully established by the initial
grLoadScreen(...)path insideislandInit() - this collapses the remaining causal surface tightly onto:
adsInitIsland()islandInit()- specifically the initial
OCEAN0?.SCR/NIGHT.SCRbackground load and the state path that leads there
- Cleanup:
- reverted the temporary early return after
grLoadScreen(...)
- reverted the temporary early return after
- Next target:
- determine why exact
ACTIVITY.ADS 1is reaching that background-load-only island path and never replacing it with live ACTIVITY scene content
- determine why exact
18:45 PDT - Skipping exact scene island-state calculation perturbs the lead-in, but ocean still settles by 04910
- Temporary proof:
- in
repo:/story.c, skipped:storyCalculateIslandFromScene(scene)
- only for exact
ACTIVITY.ADS 1 - left:
adsPrimeSceneResources(...)adsInitIsland()- normal intro flow
- normal later playback path
- in
- Validation run:
repo:/regtest-results/activity1-ads1-seed1-skip-scene-islandcalc/result.json
- Result:
- run exits cleanly with:
state_hash = 81b56aa4b51befe5badf1bdfff881504bc469fc5a829e1924a706c359620cf03
- sampled frames:
frame_04770: blackframe_04910: oceanframe_05000: ocean
- run exits cleanly with:
- Important conclusion:
- scene-specific island-state calculation does affect the lead-in before the settled failure
- but it does not prevent the stable wrong-ocean state from being re-established by
04910 - so it is not sufficient to explain the persistent ACTIVITY failure on its own
- the strongest remaining causal surface is still:
adsInitIsland()- especially
islandInit()and its initial background load/composition path
- Cleanup:
- reverted the temporary skip-scene-islandcalc proof patch after validation
- Next target:
- isolate
adsInitIsland()/islandInit()more directly, because that is now the tightest surviving source of the settled wrong-ocean outcome
- isolate