Commit Graph

13 Commits

Author SHA1 Message Date
Sylpheed port agent
e35c9453b7 port: S00A is closed as not obtainable, and Q5's shape is tighter than "unstable"
Three things folded in from the Decoder, none of which change the export.

S00A IS NOT OBTAINABLE HERE and it is the game that stops it, not the drive. The
drive works end to end -- main menu +0.999, newgame-difficulty +0.999,
newgame-selectdata-crash +0.997, with the focus detector validated live against a
known transition rather than trusted -- and then the guest throws at
PC: 0x82307128 x349 with no S00A stream ever decoding. Recorded in BLOCKED as a
route finding, with the consequence for me stated: THE CENTRE-CHANNEL RESULT
RESTS ON ADV ALONE. S00A was wanted precisely because its second stream is
digital silence where ADV's is a 0.60x copy.

Q5 GAINED TWO POINTS and the shape is sharper than "it varies": TUTORIAL x3, NEW
GAME x3 over six boots, and no other item ever observed. That does not change the
port's authored initial focus -- NEW GAME is still one of exactly two observed
states and is the state of the committed capture -- but it changes what would
REFUTE it, so the authored `why` now says so: a boot opening on LOAD GAME,
OPTIONS or EXTRAS breaks the two-way shape, and a rule predicting the split
deletes the entry outright.

`which-focus` NEEDS GODOT AND THE EXPORT TREE, so it cannot run in the RE
container. Recorded in its own header rather than left for the next agent to
discover, along with the pointer to the capture-only alternative and that tool's
own caveat: its offline controls are its calibration inputs, which is
self-consistency and not validation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 17:54:12 +00:00
Sylpheed port agent
28d90449e6 port: which-focus -- a focus detector for the Decoder, with the control wired in
S00A is blocked on knowing which button a screenshot has focused.
`newgame_path.sh` assumed NEW GAME at boot, drove on it, and landed in a tutorial
mission -- HANDOFF Q5 measured focus as UNSTABLE across boots. Counting presses
cannot substitute: up from the first item wraps to the last, so no fixed number
of presses lands on a known item from an unknown start.

The Decoder's own attempt, a per-row brightness statistic, FAILED the control --
it picked NEW GAME on the capture whose filename says OPTIONS. The
render-difference method passes it, so this packages it as a script.

IT RUNS THE CONTROL ON EVERY INVOCATION, not once when it was written, and
refuses to report anything if the control fails.

  live-main-menu-options-focused  KNOWN ANSWER      OPTIONS         4.7x
  live-main-menu                  the question      NEW GAME       11.4x
  live-extras                     KNOWN from corpus MISSION SELECT  4.2x
  live-title-press-a              no menu at all    refuses         1.0x

The extras row is a second known answer I did not plant -- authored/flow.json
already records "MEASURED: EXTRAS opens focused on MISSION SELECT
(live-extras.png)" -- and the tool reaches it independently. The title row is the
negative control.

AND THE REFUSAL NOW CARRIES A NON-ZERO EXIT CODE. The first version printed "do
not act on this" and exited 0, so a caller scripting it -- which is the entire
point -- would have read a refusal as an answer. Same defect as a checker
claiming a check it skipped, and the fifth instance of that shape this session.

What it is not: it identifies focus in ONE FRAME and says nothing about what
selects focus. Q5's instability stands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 17:29:48 +00:00
Sylpheed port agent
4e249d9d63 port: the capture has NEW GAME focused, and my harness had been rendering no focus
The Decoder attached an honest caveat to its reproduction of the tone-curve
refutation -- its bins included the focus-state mismatch, so it was "not a clean
second opinion". Removing that caveat found a defect of mine.

IDENTIFYING THE FOCUSED BUTTON, with a known-answer control. Walking focus
through all five buttons and comparing each against a capture:

                    vs -options-focused    vs live-main-menu
  ptbtn01 NEW GAME        6351                   531
  ptbtn02 LOAD GAME       7087                  7094
  ptbtn03 TUTORIAL        6230                  6237
  ptbtn04 OPTIONS         1292                  6364
  ptbtn05 EXTRAS          6073                  6080

The control picks OPTIONS by 4.7x -- the answer its own filename gives -- so the
test is trustworthy, and live-main-menu.png has NEW GAME focused by 11.5x. That
is what the port already focuses. Corroboration, NOT a decode: Q5's instability
stands (four boots gave TUTORIAL, TUTORIAL, NEW GAME, NEW GAME) and this
identifies one frame rather than a rule. Recorded in the authored entry.

THE DEFECT: `verify-capture` rendered menus with `--screen=`, which draws no
focus record, so it had been comparing main_menu to the oracle in a state the
oracle was never in.

  main_menu  0.25% differing, RMSE 14.79  ->  0.06%, RMSE 13.21
  extras     0.46%, RMSE 15.29            ->  0.20%, RMSE 13.38

A 4x improvement that was entirely my harness posing the port wrong rather than
the port drawing wrong. The first run of a new correctness harness reported a
discrepancy and three quarters of it was the harness.

AND THE DECODER'S CAVEAT RESOLVES TO NOTHING, measured rather than assumed:
re-deriving the transfer curve on the correctly-posed pair gives 1.20 / 1.26 /
1.18 / 1.10 / 1.03 / 0.94 at render 8..48, unchanged from the mismatched run. Its
reproduction stands as a second opinion after all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 17:23:27 +00:00
Sylpheed port agent
ca23a5479a port: build the correctness harness verify-screen has pointed at since P1
`tools/port/verify-screen` line 20 has said "use `tools/verify-capture` for the
correctness question" since P1, and there was no such file. The port has had a
harness comparing itself to sylpheed-cli -- two renderers sharing its assumptions
-- and none comparing it to the game, while its own docs said otherwise. That gap
is what ORACLE-CAPTURES.md warns about: this corpus has been bitten three times
by two renderers agreeing, and every one was obvious against a capture.

Five screens against framebuffer captures of the real game:

  main_menu        RMSE 14.79   0.25% differing   focus state may differ
  extras           RMSE 15.29   0.46%             focus state may differ
  title            RMSE 21.07   1.82%             ptloop sweeps never stop
  publisher_logo   RMSE 10.77   1.00%
  developer_logos  RMSE  9.37   0.39%

NO SCREEN SHOWS A LARGE CONNECTED BLOB -- the shape a missing element makes, and
the shape all three historical failures made.

And 74.1% of main_menu's differing pixels fall inside the ORACLE'S OWN focus
signature (live-main-menu vs live-main-menu-options-focused, the same screen with
a different button lit). So the bulk of that disagreement is a state mismatch,
not a defect.

REFUTATION ATTEMPT, on ui-render-tone-curve.md's `capture = 255*(render/255)^g`.
It survives where it was measured and not past it. Binning every structurally
matched pixel by render level gives the relationship directly, and the implied
exponent is NOT constant: 1.26 at render 16, 1.10 at 32, crossing 1.0 near 44,
down to 0.69 at 96. Above ~44 the capture is BRIGHTER than the render, which one
exponent cannot express -- and that is exactly why my whole-frame fits kept
returning 1.00, the two halves cancelling. The page's own stated reach ("nothing
constrains midtones or highlights") was not a hedge, it was the finding. Its 1.49
for this screen measures 1.18-1.26 in my darks; recorded as a disagreement rather
than resolved, since they fit selected flat patches and I binned everything.

Two earlier versions of this tool reported a best-fit gamma and were wrong both
times -- once fitting across a 74% structural mismatch, once extrapolating past
the measurement's stated reach. The fix was not a better fit but a different
instrument: it prints the curve, which somebody can argue with.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 17:18:14 +00:00
Sylpheed port agent
33bb50e80d port: stripping driver padding is exact -- the gate on S00A is cleared
The Decoder made this the gate on S00A and that was the right order: ADV plays
itself on boot and can be captured with --gpu=null at 0.96x real time, but S00A
starts ~4.5 s after (A) on a save slot, which needs a driven run, which needs
screens, which rules out --gpu=null. So S00A is necessarily the 0.70x rendered
route with ~10% additive padding, and is only worth a boot if stripping is exact.

It is. A real music+SFX bed -- 137.37 s, carrying 454 genuine zero runs of its
own -- had 1149 holes inserted at 8.37/s to +9.9% length, matching the observed
ALSA profile, then was stripped and correlated in the low band:

  original vs itself      r 1.000  lag   0.0 s  margin +0.141   ceiling
  PADDED   vs original    r 0.436  lag -12.2 s  margin +0.006   destroyed
  STRIPPED vs original    r 1.000  lag   0.0 s  margin +0.142   recovered
  stripped vs orig-also-stripped   r 1.000      margin +0.143

Two things worth reading off that. Padding at that profile destroys correlation
completely -- r 0.436 in the known-absent regime -- which independently confirms
on a file whose contents I control that the earlier captures were unusable for
the reason claimed and not for some other one. And recovery does NOT require
stripping both sides: the stripped capture matches the UNSTRIPPED source at the
ceiling, so the port's reference assets never need touching.

`tools/port/strip-padding` implements it, and its header leads with when the
operation is vandalism rather than with what it does: PulseAudio's monitor
SUBSTITUTES silence and deleting those holes repairs nothing, while Xenia's ALSA
writer PADS and removing that is exact. Running it on the wrong artefact would
look like it worked.

Its output is byte-identical to the control's own stripping, so the tool and the
experiment are one operation rather than two implementations that agree.

Not licensed by this: stripping removes genuine silence too and cannot tell them
apart. Here the genuine runs total 0.71 s in 137 s and cost nothing measurable;
on material that is mostly silence they would.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 17:04:05 +00:00
Sylpheed port agent
78879dce06 port: check-capture refuses a format it cannot read, and accepts the one it nearly rejected
The Decoder is moving to an ALSA `type file` tee, which writes float32. Read as
s16 that yields a plausible-looking file whose only tell is per-channel peaks
alternating exactly -- the two halves of each float landing in alternate
channels. My parser assumed s16 throughout and would have mis-read it
confidently.

An unreadable format now ends the run at PARTIAL (exit 2) rather than PASS:
channels were checked, starvation was not, and the tool says which. A checker
that claims a check it skipped is the shape of every failure this file documents.

AND THE FIRST VERSION OF THE GUARD WAS TOO STRICT -- it rejected one of this
tool's own controls, a six-tone file `ffprobe` correctly calls pcm_s16le, because
the file is WAVE_FORMAT_EXTENSIBLE (tag 0xFFFE) rather than plain PCM. A format
guard that refuses a legitimate capture is the same defect as one that mis-reads
an illegitimate one, pointing the other way. The check turns on wBitsPerSample,
which is what decides the layout; a float tee is 32-bit and still caught.

Control sweep, now the tool's real specification and all of it runnable here:

  real music+SFX bed                        PASS
  voice track, mono, 53% real pauses        PASS
  six distinct tones, PCM and extensible    PASS
  bed with 350 ms holes punched in          FAIL
  the starved capture                       FAIL
  the same tones as float32                 PARTIAL

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 16:55:19 +00:00
Sylpheed port agent
ba364dba5a port: check-capture needed two numbers -- the rate alone passed a 50%-silent file
The Decoder found a blind spot in the bar I shipped last iteration. Raising the
PulseAudio client buffer keeps cutting the gap RATE while total silence bottoms
out and then doubles -- an over-large buffer starves in a few enormous holes
instead of many small ones. Its 500 ms capture scores 1.3 gaps/s, better than a
genuine music bed at 3.3, while being 50% silence. My 20/s bar passed it.

Same shape as the level table that cannot see a duplicated channel: one number,
blind to the failure next door.

I did not set a bar on their numbers, because I do not hold those files and the
last two bars in this tool were wrong precisely from being invented. Instead I
built a control in that regime -- `bigholes`, a real bed with 350 ms holes
punched in -- and set the rule from four controls I can run:

  real music+SFX bed          1.1% silence,  3.3 gaps/s   PASS
  voice track, mono, pauses  53.2% silence,  0.3 gaps/s   PASS
  bed with 350 ms holes      46.3% silence,  3.2 gaps/s   FAIL
  the starved capture        35.6% silence, 30.9 gaps/s   FAIL

Rate alone cannot separate rows 2 and 3; silence alone cannot separate 1 and 3.
The pair does: fail when >=10% is silent on every channel AND there is at least
one gap per second. Real audio is either mostly not silent, or silent in a few
long stretches -- not both at once.

AND THE REGIME IT STILL CANNOT JUDGE IS PRINTED RATHER THAN PASSED. High silence
with very few gaps is what a real voice track looks like and what an
over-buffered capture looks like; nothing here separates them, so the tool says
UNJUDGED and tells the reader to check against a known source. Inventing a bar
for a regime with no control in it is how the previous two bars came to be wrong.

A CONTROL THAT DOES NOT EXECUTE IS NOT A CONTROL: the tool returned immediately
for single-channel input, so the mono voice track -- one of the four controls --
was never run through the check it was meant to control. Mono now skips only the
duplicate test.

Also recorded: the Decoder has withdrawn "the monitor-sink route cannot be fixed
by configuration". A ~200 ms client buffer is worth a retry BEFORE anyone spends
a session on a Canary rebuild.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 16:40:24 +00:00
Sylpheed port agent
57bb630c69 port: make the dwell comparison repeatable, and record that the settle run is unanchored
TWO THINGS, and the first is that nothing needed changing.

The Decoder withdrew one of the two legs under its settle-time run: the plate
pulse period it had offered as proof the run was not slowed rests on one interval
at a 125 ms sample rate, and re-picking the troughs gives 2.628 s rather than
2.369 -- an adjacent local minimum counted as a separate trough. It cannot
resolve a real-time factor below ~7%.

Nothing in the port moves, because the numbers that correction touches were
already unauthored. Checked rather than remembered: grep over authored/ and
port/scripts/ finds no 0.531 and no 0.482. The only build-in reference in the
tree is the plate arithmetic t=118 -> t=238, 120 units, which is the anchored leg
-- it agrees with three prior readings and with the disc's own declaration.

I had declined those two as one-run figures the Decoder itself flagged, with the
port already within ~0.1 s from the disc's keyframes. That reasoning now has a
second, independent justification I did not have at the time: a few per cent of
slowdown sits inside them undetected.

SECOND: `tools/port/verify-dwell`. Last iteration's hand comparison refuted a red
flag I had filed myself -- `rest.t` is the wrong settle landmark, but "everything
the sequencer paces off it is therefore late" was false and I nearly re-paced
screens that already matched the game to 0.05 s. That check existed once, in a
transcript. Now it runs.

Its header carries the trap it exists to prevent, because that is the whole
point: a port's TRANSITION TIMESTAMPS and the oracle's VISIBLE SPANS are not the
same quantity, and differ by the exit ramp plus the black hold -- about 0.6 s,
the entire discrepancy. The same confusion cost this corpus 0.48 s on the plate
delay.

The bar is the oracle's own run-to-run spread plus one film interval. Three cold
boots of the real game differ by 0.3 s, so agreeing more tightly than the oracle
agrees with itself would mean nothing. The developer-logo span reads 3.50 s on
the hand-run and 3.75 s here, one interval apart and both inside the bar -- the
tool reporting its resolution rather than hiding it. The oracle's numbers are in
the script as a labelled test fixture citing their RE document; nothing in the
port derives them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 16:34:06 +00:00
Sylpheed port agent
c184a4f8ab port: check-capture passed a file that was 36% holes -- it now catches starvation
The Decoder diagnosed take 2 as a STARVED capture and I verified it here rather
than take it on trust: 35.6% of frames silent on all six channels, 10482
alternating runs, median burst 13.5 ms and gap 3.9 ms, a 17.4 ms period at 57 Hz.
Their untruncated original reads 39.3% and 10595 runs; the difference is exactly
the truncation and every other number agrees.

So my rebuilt correlator was working correctly on a file that could not carry the
signal. The alarming reading it produced -- that the game may not play the .wmv's
WMA track, so ADV.ogv's audio has been wrong since P4 -- is NOT SUPPORTED by this
capture and is not refuted either. Withdrawn as a concern arising from evidence,
with nothing changed in either direction. It was the most expensive-to-act-on
hypothesis in the port and it came from a file that could not speak to it.

THE REAL DEFECT WAS MINE: `check-capture` tested only for duplicated channels, so
it cleared a recording that was 36% holes. A provenance check that passes the
artefact it was built in response to is not a check.

It now measures starvation, and TWO THRESHOLDS I INVENTED WERE BOTH WRONG:

  counting exact-zero frames -- real audio crosses zero constantly, so a clean
    voice track scored 5947 "gaps" of median 0.0 ms and was called starved. A gap
    is a RUN, not a sample; only runs over 1 ms count.
  gap count and median length -- a genuine music bed shows 454 gaps at a median
    of 1.4 ms, because quiet 16-bit passages really are zero for milliseconds.

What separates them is the RATE: 32.9 gaps/s starved, 3.3 for a real bed, 0.03
for a voice track that is 53% pauses. Bar at 20/s, derived from those controls
rather than chosen and then justified. Controlled both directions: real stereo
bed PASS, six distinct tones PASS, starved capture FAIL. It also reports a `data`
chunk declaring 0 bytes -- what a file copied mid-write looks like, which is what
happened.

VOICE CHANNEL ROLES ARE NOT OBTAINABLE THIS SESSION. Both routes closed: the
monitor sink is starved by construction, and the internal tap at
SDLAudioDriver::SubmitFrame needs a Canary rebuild the Decoder has costed at a
whole session. That is the human's call, not an agent's. The port keeps authoring
with the known recorded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 16:25:17 +00:00
Sylpheed port agent
f23180c4eb port: a capture must pass a provenance check before anyone analyses it
The 6-channel capture I spent an iteration refuting was corrupt, and the Decoder
found the cause: PulseAudio remapping between two mismatched channel maps, where
a 6-channel remap SILENTLY DROPS AND DUPLICATES. Its control -- six channels, six
tones, same sink, same parec -- came back 400/3200/200/800/800/200 for an input
of 400/800/200/1600/3200/6400. Two source channels gone entirely.

So the negative was right, and the byte-identical pair I reported was the thread
that unravelled it. Worth recording precisely, because it nearly went unreported:
it began as an idle look at two channels whose peak AND RMS matched to six
decimals, and it only became evidence because a coincidence at six decimals is
cheaper to hash than to explain.

`tools/port/check-capture` makes that check one command -- split the file, hash
every channel, fail on any duplicate pair -- and AUDIO-VERIFICATION.md gains a
section 5 saying to run it FIRST, every time, plus the two conditions the same
incident produced: start the recorder before the process, and log what was on
screen against the recording's own clock so a miss is diagnosable.

Controlled both directions, because a checker nobody controlled is what this
incident is about: six distinct tones PASS; the remap's own output pattern FAILS
naming all four pairs; the corrupt capture FAILS on ch2 == ch5.

THE KNOWN-BAD CONTROL IS THE POINT. All six of its channels report a peak of
-18.063656 dB, identical to six decimals, while containing three duplicate pairs.
A level check cannot see this failure. That is why the tool hashes rather than
measures, and why the corrupt capture's "plausible per-channel levels" were never
evidence. The tool says of itself that it is necessary, not sufficient.

Withdrawn with the file, both the Decoder's: "all six channels carry signal", and
the non-zero-surround observation offered as weak support for 5.1. Unaffected:
the three-XMA-context concurrency result, read from the emulator's log rather
than the audio path, on two independent boots.

The corrupt file is dropped from the exchange so the next agent cannot pick it up
and repeat the work. Nothing in the export changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 16:07:19 +00:00
Sylpheed port agent
8fba7944d4 port: the game decodes all three voice streams at once, and two baseline rows were comparing blank frames
TWO FINDINGS, one mine and one handed to me, and the second retires a premise I
built on twice.

THE P1 BASELINE HAD ROWS THAT PROVED NOTHING. `build_12` and `build_15` render
pure black in BOTH renderers -- mean 0, max 0 -- so the difference is zero and
`verify-screen` scored them `max 0  over3 0  OK`, the strongest verdict it has.
Two of sixteen rows were comparing nothing against nothing. Worse than a missing
test, because a missing test is visible in the count.

Cause isolated by a control, not by reading: `build_00`/`build_01` are the same
loading screen minus three elements and render fine (mean 1.913, max 214.5). The
dressed variants add `pgloading_eff00`, a 1280x720 primitive resting OPAQUE BLACK
at t=38 inside its own opening black hold, with no layer key so paint order puts
it last.

The rule I was about to write -- "rest.t before the last timed keyframe is the
pathology" -- was killed by running the census first: 152 of 212 elements in this
export have rest.t earlier than their last timed keyframe. It is the norm. What
is actually unusual is the CONTENT, and its reach is one: `pgloading_eff00` is
the only element in the export whose resting pose is a fully opaque full-frame
quad. One instance is not a rule, so the renderer is unchanged and the HARNESS is
fixed: a blank pair now reports BLANK -- both renderers drew nothing; this row
proves nothing. `status` is untouched, so an unrelated DIFFERS still fails.

THE VOICE EXPORT IS KNOWN INCOMPLETE. The Decoder booted Canary with
--xma_param_probe and the game decodes ALL THREE streams CONCURRENTLY, in three
XMA contexts whose byte sizes match the disc payloads exactly. So "three
presentations of one take, pick one" is refuted by the running game and the
question I had been arguing -- WHICH presentation -- has no answer.

This one no census could have caught. Every measurement was right: the streams
are equal-duration, one is silence, one is 0.60x another with the residual 26.8
dB down. The frame around them was wrong, and the file says ChannelMask 0x0002 on
all three. It took the running game -- which is the mission's own sentence
arriving in practice.

BEHAVIOUR HELD DELIBERATELY. An equal-gain 1/n sum of channel pairs is not a
downmix either -- MISSION section 6 pins an explicit matrix for exactly that
reason -- and summing cost S00A 6.02 dB when one stream was silence. Swapping one
guess for another on a message is what produced this entry twice. What changed is
that the wrongness is now LOUD, because this failure sounds like success: one
stream is clean audible dialogue. A top-level manifest warning per movie, the
console line, and the authored entry all say `1 of 3 streams`.

"They are 5.1" is recorded as the Decoder's HYPOTHESIS with its own
counter-evidence attached, and nothing builds on it. What settles it is asked: a
recording of the game's own output over ADV through the null sink, which turns
channel roles into a fit against an oracle.

Refutation attempt, survived: the Decoder's loading-screen variant map. Entries
0/1 carry 7 elements and 12/15 carry those seven plus baseeff, eff00 and loop5 --
exact in count and identity, and it is what made build_00 a control.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 15:50:02 +00:00
Sylpheed port agent
9bd7297757 port: the P1 regression harness could not have run since the monorepo merge
verify-screen resolves its reference binary to a path build-reference-cli
stopped being able to produce: that script greps Cargo.toml for a
`Syplheed-Reborn.git", rev = "..."` pin, and 9fbb352 replaced it with a path
dependency. The grep returns nothing, the script exits 1, and the binary left
behind predates the merge -- three hours older than the sources, from a revision
nothing points at. The consumer only checked that the file EXISTED.

DECISIONS already carries "the reference renderer was stale for three diff runs"
from P2. This would have been the fourth, by a different mechanism: not a
forgotten rebuild but a build step that could no longer succeed.

The fix is a deletion. Revision-keying solved a two-repo problem -- /reborn's
target/ was a live mount that moved mid-run -- and the monorepo removed that
problem by construction. verify-screen now builds sylpheed-cli from the
workspace, which is the same decoder the exporter and the port read.

BASELINE, all 16 screens, after four milestones of renderer change: no new
drift. The only substantial disagreement is title_jp (max 155, 20498 px), which
is the same one P1 recorded and left open -- ptlogo_eff2 at 125 % scale, where
blit samples the destination pixel's corner and a GPU samples its centre. The
port still does not match it, because matching means reproducing a half-pixel
bias on purpose to make a number smaller.

`max` alone could not tell 2 pixels from 25 000: main_menu and title_jp produced
the same DIFFERS verdict. The script now also reports how many pixels are over
the bar. The bar is NOT raised -- tuning a threshold until things match is the
failure its own header warns about.

Stated plainly because sixteen OK rows look reassuring: the harness renders
--pose=rest, so NONE of the last four milestones' visible work is under test --
not the spinning ring, not the plate composite, not any timeline behaviour, not
audio.

REFUTATION: the RE agent's answer naming "builds 0/1 and 10/11" as the loading
screen is false in the index space this export uses -- entries 10 and 11 are
palogo_sqex and palogo_gamearts, the splashes, which the same agent identified
as such answering my ask 1. Over the twelve is_build bundles, ordinals 10/11 ARE
entries 12/15, so the finding is right and the enumeration is not mine.
authored/screen_names.json is keyed by ENTRY, and someone writing keys "10" and
"11" from that message would name the publisher wordmark and the developer logos
as loading screens, and everything would still validate and run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WM5XL4HfrHuxz8RiMWdCMC
2026-08-29 14:10:16 +00:00
MechaCat02
9fbb352ef0 monorepo: one repository for the decoders, the port and the corpus
Merges the Godot port into the reverse-engineering repository, preserving both
histories -- 1019 commits of corpus plus the port's 31, brought in by subtree
merge and then moved into place so git can follow each file across the rename.

The reason is not tidiness. The two-repo split forced the exporter to depend on
the decoders by pinned revision, and that created a whole class of failure that
now disappears: a sha reachable only from a topic branch, orphaned by a
squash-merge, breaking a fresh checkout silently at build time. It also forced a
live read-only mount of one agent's working tree into another's container, which
is why a contract file could move mid-iteration. With a path dependency, a
decoder change and the exporter change it requires land in the same commit or
not at all.

Canary stays separate: it is a fork tracking upstream.

New structure for the long term:

  docs/game/     how the game is NAVIGATED -- menus, modals, prompts, alerts,
                 and in-game flight. Written so nobody rediscovers it. Mostly
                 open questions on purpose; the in-game tutorials are the
                 resource for the flight half.
  docs/port/MODDING.md
                 modding as a constraint on the exporter TODAY, not a later
                 feature: one logical asset in one file (the disc splits nearly
                 everything, and resolving that is the exporter's job), names a
                 person recognises, PNG/OGG/OGV/JSON only, base-and-overrides so
                 re-exporting is always safe, provenance in every file.
  data/base + data/mods
                 generated tree and drop-in overrides, both gitignored
  exchange/      transient inter-agent files, deliberately outside history
  docs/agents/   the team protocol

Both the README and the navigation doc lead with the correction that cost the
most: the oracle is the real game under Xenia Canary. Reborn's renderer is a
hypothesis under test, it has been wrong, and treating it as ground truth
propagated into three documents and both agents before a human caught it.

Scripted modding stays possible without being built: no screen name is hardcoded
in GDScript and there is no native code in port/, which is what Godot Mod Loader
needs to be able to substitute behaviour later.
2026-08-29 11:34:46 +02:00