Commit Graph

1432 Commits

Author SHA1 Message Date
sylph-decoder
4ad4ca3680 retro: the Decoder side, and the integrity check it forced
The human asked both agents for a critical retro; it reached me relayed through
sylpheed-port, and I am treating it as a message rather than as their word while
staying paused on RE iterations.

Worst failure first: I READ the refutation that mattered and routed around it
instead of auditing it. REFUTED.md killed the blend bit with our own renderer as
its instrument, and I quoted the 'a claim resting on our renderer is a claim
about our renderer' rule at the port in the same session while not applying it to
my own register.

Underneath it is the sharper one: I twice accepted render-derived labels for a
disc-side question. My 'no field separates them' negative was tested against a
partition that was wrong in six places, all six of which the oracle later called
additive.

Also recorded: the silent vertex truncation, a coverage claim written rather than
computed and wrong by four, a batching generalisation refuted by the log it was
written from, and navigation whose fix was worse than the bug.

Two additions to the port's list of shared costs: neither of us has ever given a
negative a positive control, so 'absent' and 'my search does not work' are
indistinguishable in every undecodable page I have written; and we keep
attributing a three-way residual to whichever leg we happen to be looking at.

Their eight proposals attacked one by one -- P2 sharpened into a
re-classification rather than bookkeeping, P5 pushed back on ('suppression
localises disagreement; only the oracle labels it'), the rest agreed with
amendments.

And the check the retro forced: my rival sweep covered the T8aD header and not
the 60-byte declaration entry, whose earlier hunt used the corrupted labels. Swept
properly, 16 of the 35 measured elements have no declaration entry at all and 0
declaration bits separate the other 19. The decode is not underdetermined -- run
because it could have gone the other way.

The PROTOCOL delta is presented, not applied.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 11:08:34 +00:00
sylph-decoder
d9cdfff0a8 handoff: the blend is a bit on the disc, and their three asks
Delivered: T8aD +0x04 bit 0x02 selects additive, so the port derives the blend
instead of transcribing my table -- including for screens neither of us has
captured. With why I got it wrong first: the bit was already in REFUTED.md,
killed by a comparison of two renders made while our renderer had a stale
keyframe association, no leaf geometry and no rotation.

Their asks: pteff10 IS additive on the main menu, three sessions, every frame,
and my coverage table listing it uncovered there is corrected. The sweep ramp is
four leaf keyframes on the disc, with the oracle agreeing on sign and magnitude
and a second identification from the sub-128 alpha. And kind & 0x2 is not mine
to grant -- stated what the decode supports (0 violations in 15493 entries, and
it means the declaration marks the element focusable, not that the cursor can
reach it) and left the adoption to them.

ui-blend-mode-measured.md is superseded in its classification and keeps its
measurements, which are what the decode is fitted to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 07:20:50 +00:00
sylph-decoder
bac8e17a7f re: the blend mode IS on the disc -- T8aD +0x04 bit 0x02, DECODED
Reverses two of my own pages. t32-blend-mode-not-on-disc.md said the mode is not
on the disc; ui-blend-mode-measured.md classified it measured and told the port
that which field selects it was unknown. Both were honest and neither is current.

Bit 0x02 of +0x04 set means the game draws that sprite ADDITIVE
(RB_BLENDCONTROL0 = 0x01010101); clear means premultiplied alpha-over.

Fit: 35 elements over three screens, 16 set/additive and 19 clear/alpha-over,
zero errors, every label read out of the guest command stream rather than off a
render.

Control: of every bit of the first twelve header words, exactly one separates
those 35 without error. Nothing ties with it -- which is precisely what the
+0x08 = 0x8050 candidate failed.

Within-pair: ptbtn00 0x0110 alpha-over against ptbtn00f 0x0112 additive, same
screen, same bundle, adjacent draws, one bit apart. And other f variants are
bit-clear and alpha-over, so it is not 'focused variants are additive'.

Out of sample: a prediction committed at 088df91, before its capture, on a
different archive -- GP_OPTIONS entry 19, 3 additive of 16, falsified if any
other element drew additive. The game drew exactly po_menu_eff01/02/03 additive
and nothing else.

This revives a claim REFUTED.md killed. The refutation said blending those
sprites additively worsens every measure against the capture -- a claim about
our renderer, made while that renderer had a stale keyframe association, no leaf
geometry and no rotation. Recorded as a revival with its evidence;
check_refuted.py passes with 0 unmarked assertions.

Two other readings of the same bit stay refuted and are explicitly not revived:
'the name contains eff' and 'premultiplied storage'. Those were the bit's
meaning; this is its effect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 07:20:12 +00:00
sylph-decoder
d694bcc53f re: the sweep ramp is DECODED -- four leaf keyframes, and the GPU agrees on sign and magnitude
The port asked for the sweep strips' vertex alpha as a function of position. It
is not a runtime curve to sample: ptloop01/ptloop02's nested leaves declare four
keyframes each, with x, alpha, rotation, scale and time.

  pteff03   loop 600u  x -639 -> 1521, alpha 255 -> 128 -> 255, rot +30, sy 600 %
  pteff03a  loop 720u  x 1721 -> -839, alpha   0 -> 128 -> 255, rot -45, sy 800 %

Checked against three sessions of GPU draws: both signs right every time, and
magnitudes within ~15 % of the declared slopes.

The independent bit worth having: across sessions the measured alpha spans
45..242, and only pteff03a declares alpha below 128 -- pteff03's ramp never
leaves [128,255]. The strip measured at 45 is the 1303-tall one, which the AABB
geometry says is pteff03a for a completely different reason. Two identifications
agreeing.

Stated as limits rather than buried: this cannot separate the two declared
slopes, which are 25 % apart against a quantisation of 6.4 px and one alpha
level over 3-4 frames; and absolute phase is unchecked because the AABB-left to
element-x mapping under rotation and pivot is not established.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 07:11:09 +00:00
sylph-decoder
3d8b1c2d4b tools: the sweep leaf's declared ramp, and the slope measured off the GPU
sweep_leaf_ramp dumps the nested ptloop01/ptloop02 leaf keyframes -- position,
alpha, rotation, scale and time -- which is where the ramp the port asked for
actually lives.

sweep_positions now also pools alpha against position per strip and prints the
slope, with the quantisation stated: NDC prints to two decimals, so one frame's
dx is 6.4 px and alpha is one level, and at three or four frames the two
declared slopes (+0.0814 and -0.0651) are inside that noise. It is a direction
and magnitude check, not a discrimination.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 07:10:35 +00:00
sylph-decoder
871e80ba85 tools: reach OPTIONS by the WRAP, not by counting presses
options_draw_capture.sh counted three DOWNs. A dropped press lands the cursor on
TUTORIAL, whose (A) starts the tutorial. My two attempts to avoid counting were
both worse: 'press until the cursor stops moving' is unreachable on a wrapping
menu, and an earlier version of it read the same row twice after a lost press
and pressed (A) on NEW GAME.

The wrap is the landmark. Press down until the row DECREASES -- the cursor is
then on item 1 whatever it started on -- and take three steps, retrying any
press that does not move the cursor, aborting rather than pressing (A) if one
never lands. Needs the row to be monotone in the item, not calibrated.

Also: 1200 s title deadline, and the emulator is killed at the end.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 07:06:49 +00:00
sylph-decoder
088df913b2 re: T8aD +0x04 bit 0x02 predicts the MEASURED blend, 35/35 -- and a prediction to test it
REFUTED.md kills this claim: 'T8aD +0x04 bit 0x02 selects an additive blend ->
mine, and refuted. Blending those sprites additively worsens every measure
against the capture.' That refutation rests entirely on our renderer, which the
corpus's own rule calls a hypothesis under test. The blend is now measured off
the GPU, so the claim can be tested against the oracle.

35 elements over three screens, every label an RB_BLENDCONTROL0 value read from
the command stream: 16 bit-set and additive, 19 bit-clear and alpha-over, zero
false positives, zero false negatives.

The control that makes it a decode rather than a coincidence: of every bit of
the first 12 header words, EXACTLY ONE separates those 35 elements without
error. Nothing ties with it. A perfect partition on a small sample is worthless
if half the header partitions equally well, which is the mistake +0x08 = 0x8050
was.

And the pair no confound survives: ptbtn00 = 0x0110, ptbtn00f = 0x0112 -- the
PRESS (A) plate and its own highlight, same screen, differing in exactly this
bit, drawn alpha-over and additive respectively.

Committed alongside is a PREDICTION for GP_OPTIONS, written before the capture
that tests it: a different archive, a different element set, and a MIXED
prediction -- po_menu_eff01/02/03 additive, 592 elements alpha-over. Falsified
if those three draw alpha-over or anything else draws additive. The developer
splash was considered first and rejected as a test: both its elements predict
alpha-over, so it can fail but cannot discriminate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 07:06:07 +00:00
sylph-decoder
6267245920 method: a truncating instrument, and an unreachable stop condition
The UI draw capture printed 8 vertices = two quads per draw, so a batched draw
dropped the rest silently. Four EXTRAS elements therefore appeared in no draw on
any screen, which reads as 'the game does not draw these' -- and the port spent
an iteration measuring them as the worst on the screen and asking about them.
When an instrument says an element never appears, check its limits before
believing the game.

And 'press down until the cursor stops moving' is not a stop condition on a
menu that WRAPS: it was unreachable, the loop only exited by exhausting its
budget, and it landed on EXTRAS because a dropped press cancelled one lap. An
earlier version of the same loop pressed (A) on NEW GAME after two identical
readings caused by a dropped press.

What held throughout is the part worth keeping: verify the state you measured,
never the actions you took.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 07:00:41 +00:00
sylph-decoder
78a29e01fa handoff: the standing ask answered, the title, the sweeps, and my wrong coverage claim
ptframe4, pteff21/22/23 and pteff10 are ADDITIVE, measured. They were missing
because Canary's vertex dump was capped at two quads, not because the game does
not draw them.

The title: everything alpha-over except the sweep strips and ptbtn00f, the PRESS
(A) plate's focused variant, whose base is not -- which is what the pulse is made
of. And ptlogo_back2 / ptlogo_back2eff are alpha-over despite having every
surface property the ptframe* elements have, so the obvious generalisation is
refuted on the one screen that could test it.

The sweeps are on screen on the main menu and move between frames, in two
sessions at different phases.

And the coverage claim they checked was wrong -- five elements, not one -- with
the correction and their 'every button' point both recorded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:59:46 +00:00
sylph-decoder
fa887b4e5f re: EXTRAS complete -- ptframe4, pteff21/22/23 and pteff10 are all ADDITIVE
The four elements the port measured as the worst on EXTRAS, and which appeared
in no draw, were in a draw all along: the 24-index additive batch holds six
quads and Canary printed the first two. Cap raised to 64, screen re-captured,
all six named. Same draw as ptframe3, whose state was already measured -- the
one-way implication doing real work.

pteff10 is identified too, and it needed the resting SCALE: it ships as 409x144
and is drawn at 200 % x 500 % = 816x720. The matcher's 'try 1x and 2x' rule
could not name it at any scale and reported a near miss against something else,
which is a failure wearing the clothes of an answer. Candidates are now the
declaration's pivot*2 scaled by the resting keyframe as well as the texture at
1x and 2x, and the tolerance is the log's own NDC print quantisation rather than
a chosen number.

Flagged rather than buried: pteff10 measuring additive is in tension with the
port measuring it nearly exact under alpha-over. Both can be true for a dim
semi-transparent glow over a dark background, and it is the one row a rendering
check does not corroborate.

Also stated: the three full-screen alpha-over draws are NOT individually
identified -- four elements declare 1280x720 -- so the label on those rows is a
candidate, not an identification.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:59:11 +00:00
sylph-decoder
f0b4f0ad5f re: correct the blend page's reach sentence -- five elements uncovered, not one
sylpheed-port checked the coverage claim element by element and it was wrong.
The page said everything on the two screens was covered but for pteff10; on
EXTRAS, ptframe4, pteff21, pteff22 and pteff23 were also in neither the per-draw
log nor a prose row -- and they are exactly the four the port measures as the
worst elements on that screen.

The result rows were never wrong. The claim about what they covered was, and it
is the kind that reads as reassurance.

Also flagged, and fair: 'every button' is a class generalisation in the page
whose own instruction is to read it as per-element facts. It came from one
element on the main menu. Now stated as such.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:48:41 +00:00
sylph-decoder
529cf13f2a re: the TITLE's blend states, the menu replicated twice, and the sweeps are on screen
Two more emulator runs close two of the three reach limits on the blend page.

THE TITLE (entries 4 + 2 composited). Everything alpha-over except the two
rotated sweep strips and -- the finding -- ptbtn00f, the PRESS (A) plate's
focused variant, which is ADDITIVE while its own base ptbtn00 is not. That is
what the documented plate pulse is made of, and a port drawing both alpha-over
cannot reach the pulse's peak by any pacing.

And ptlogo_back2 / ptlogo_back2eff are ALPHA-OVER. They are frame-shaped, large,
dark and 94 %/87 % transparent -- every surface property the menu's ptframe* have
-- so 'frame-shaped and mostly transparent implies additive' is refuted on the
one screen that could test it.

THE MENU replicates draw for draw in two further sessions; the one-session
caveat is retired.

THE VERTEX CAP was 8 = two quads, so a batched draw reported its first two
elements and dropped the rest. Raised to 64. This is why ptframe4, pteff21,
pteff22 and pteff23 looked like elements the game never draws: EXTRAS' 24-index
additive draw holds six quads and the log printed two.

THE SWEEPS are on screen on the main menu in every captured frame, stepping
~0.03 NDC per frame in opposite directions with their vertex alpha ramping, at
different phases in two sessions. The leaf group runs on the menu. What that
does not say is how much they contribute.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:42:05 +00:00
sylph-decoder
1ce4eca706 tools: the EXTRAS walk pressed (A) on NEW GAME -- fix the stop test
'Press until the cursor stops moving' broke on its first comparison: one DOWN
was dropped, the row read the same twice, and it concluded the cursor had
stopped while sitting on the first item. It then pressed (A) on NEW GAME. No
harm this time -- neither press registered and the screen stayed put -- but that
is luck, not the design.

A stop test that cannot distinguish 'at the end' from 'the press was lost' is
the press-counting bug wearing a different hat. Now: a non-move ends the walk
only after at least one move has been observed, four consecutive dropped presses
abort, and (A) is pressed only if the cursor demonstrably moved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:40:12 +00:00
sylph-decoder
c8f488b3cd tools: separate 'submitted' from 'on screen' in a draw log
The blend map reports a quad's SIZE, which identifies an element and says nothing
about whether it is visible -- a parked quad is still a draw call. This prints
the NDC x/y range and per-vertex colour of every additive draw, per frame, so
movement between frames and overlap with the [-1,1] box are readable directly.

Written because sylpheed-port flagged exactly that conflation in my blend page:
the blend and the visibility arrived in the same artefact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:24:57 +00:00
sylph-decoder
698409fdfe tools: walk to EXTRAS by cursor movement, and size-match against several builds
menu_blend_capture.sh counted two DOWNs to reach EXTRAS, which is wrong twice
over -- EXTRAS is the fifth item, and on 2026-08-31 four DOWNs landed on OPTIONS
because one press was dropped. It now presses until the cursor stops moving,
which needs no item count and no row calibration. Its title deadline follows the
same change as title_blend_capture.sh, 1200 s not 420.

ui_blend_map.py takes a comma-separated build list, because the live title is
TWO builds composited -- 4 draws the art, 2 draws the PRESS (A) plate -- and a
one-build size table cannot name the elements of a title capture.
frame_alpha_census takes its builds from argv for the same reason.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:22:53 +00:00
sylph-decoder
020f456974 re: retract 'the game batches elements that share a mode' -- its own log refutes it
I wrote that yesterday in HANDOFF, the finding page and INDEX. It is false: in
one main-menu frame, draws 5, 6 and 7 are three separate additive draws --
consecutive, identical blend state, not merged.

Only the one-way implication holds: elements inside one draw share a blend
state; sharing a state does not put elements in one draw. The wrong version
would have licensed the port inferring a mode for an element nobody observed,
which is the one thing that page must not do. The ptframe4 conclusion is
unaffected -- it is in the same DRAW as ptframe3, not merely the same mode.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:16:37 +00:00
sylph-decoder
880a4d562b re: kind bit 0x2 is the FOCUSABLE flag -- decoded, 0 violations in 15493 entries
The declaration entry's kind word (+0x28) and its focus/nav index (+0x2C) are the
same fact twice: kind & 0x2 is set iff the focus index is >= 0. Checked over 24
UI paks and every parseable build in each -- 1062 focusable elements, 14431 not,
zero exceptions. The test is two-sided, so it would fail if any focusable element
lacked the bit or any non-focusable element carried it.

Consequence: kind == 0x3002 is not the test for a button. It catches 778 of 1062
and misses 284 (26.7 %) at 0x2, 0x2002, 0x3003, 0x73002, 0x73003 -- including
ptbtn00.rat on GP_TITLE's PRESS (A) plate, which is 0x73002. And 0x3000, 817
elements, looks like a button and is not focusable.

This is also the refutation attempt on sylpheed-port's kind census. Their claim
-- every decoration 0x0, every button 0x3002 -- is exactly right on the two
screens they checked, reproduced here independently, and fails one build over on
the title they have not run yet.

The other kind bits are reported as observed structure and explicitly not
claimed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:16:09 +00:00
sylph-decoder
01025ab494 tools: capture the title's blend states, and replicate the menu in a second session
Completes the reach of ui-blend-mode-measured.md, which was explicitly two
screens and one session and explicitly not the title.

Three things it does differently from menu_blend_capture.sh, each one paid for
yesterday: a 1200 s title deadline instead of 420 (the 420 fired while the game
was minutes from the settled title); the title arm is gated on the plate pulse
because build 2's PRESS (A) plate is part of what is being measured; and on
timeout it leaves the emulator up and says to attach rather than reboot.

Committed before it is run, per METHOD.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:11:50 +00:00
sylph-decoder
4718e90c16 refuted: my own 0x8050 candidate (twice), and the port's frame sharpener
0x8050 dies disc-wide (38 sprites, high byte tracks the archive) and again on
EXTRAS, where pteff21/22/23 share it with the frames.

The port's 'neither frame has a fully-opaque pixel' is true and is not the
discriminator: pteff10 has max alpha 130, no opaque pixel, and they measure it as
nearly exact. Their direction survives -- the draw path did answer -- so what is
refuted is the reason, not the conclusion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:09:05 +00:00
sylph-decoder
2353983df8 method: a refusing instrument, and a boot that outlasts its capture script
Two traps this run paid for.

ring_row.py's ROW0/SPACING are x11grab constants. On a /sylph-home/re/shots/shot-0001.png grab of the
same live main menu the rows read 180.5/419.5/502.0 -- ROW0 is 45 px out, 0.57
of a step. The module refused rather than naming the wrong item, which is the
good failure, and is_main_menu() therefore returned False ON A REAL MAIN MENU. A
run gated on it would conclude 'not the menu' while sitting on the menu. Not
recalibrated: three rows from one session are not a calibration and other tools
share the constants; the module now says so where the numbers are.

menu_draw_capture.sh's 420 s title deadline fired, and the emulator left running
was at the settled title minutes later, took one A, and reached the menu first
try. A timeout is a measurement of the timeout. Leaving the emulator up after a
failed script rescued this run for one minute against a twenty-minute reboot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:08:41 +00:00
sylph-decoder
0af81d7549 handoff: deliver ADDITIVE, and correct the 'any blend you choose is authored' instruction
HANDOFF gets the measured table, the two controls, the three caveats that travel
with it (src=ONE is not evidence of premultiplied textures; the frames share a
draw call so ptframe4 comes free; read it as per-element facts because the
selecting field is still unknown), and the correction to the port's own
sharpener -- 'neither frame has a fully-opaque pixel' is true and is not the
discriminator, because pteff10 has none either and renders accurately.

t32-blend-mode-not-on-disc.md keeps its negative and its reach and loses its
conclusion. INDEX gets the row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:07:36 +00:00
sylph-decoder
e61c60029a re: the UI blend mode is MEASURED -- the frames are drawn ADDITIVE
Closes the one route t32-blend-mode-not-on-disc.md left open: the executable's
draw path. Canary's UI draw capture now logs RB_BLENDCONTROL0 per draw, and the
game was driven to the main menu and to EXTRAS with F10 at each.

The title-side UI uses two blend states and ONE pixel shader:

  0x07010701  src=ONE dst=1-SRC_ALPHA  alpha-over (premultiplied)
              ptbase, pteff05, the fade quad, ptmsg, ptmsg2, pttitle, buttons
  0x01010101  src=ONE dst=ONE          ADDITIVE
              ptframe1, ptframe2, ptframe3, pteff20, both rotated sweep strips

Two controls, both run before the result was read:

* the NDC->pixel conversion that identifies a draw by its quad size reproduces
  1134 and 1303 px for the two rotated sweep strips -- numbers measured by a
  different tool in a different session -- on BOTH screens. The tool prints
  PASS/FAIL and disclaims its own output on FAIL.
* pixel shader 0xE59B2B3DA4AA9008 is used with BOTH states, 12 draws additive
  and 18 alpha-over. ptframe1 and ptbase run the same shader; only the blend
  register differs. So this is a blend result, not a shader result.

This confirms the port's independent measurement -- it solved the composite per
pixel from two backgrounds and found additive halves alpha-over's error on both
frames -- by a route with nothing in common with it.

So the blend is no longer authored: 'any blend you choose is authored' was true
of the disc and is not true of the game. What is still unknown is which field
selects it; elements sharing a mode are batched into one draw call, so the
selection happens before the draw.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:06:30 +00:00
sylph-decoder
2c7c6c255e tools: map a Canary UI draw log's blend states onto named sprites
Identifies each draw by the pixel size of its quad -- NDC extents times the
1280x720 surface -- matched against sprite dimensions read off the disc, since
the log names no elements.

Carries its own control: build 5 draws two rotated sweep strips whose heights
were measured independently at 1134 and 1303 px in
docs/re/data/title-sweep-drawn-at-rest.txt. If the conversion does not reproduce
those, every size it prints is wrong and it says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 06:02:12 +00:00
sylph-decoder
6f53c2cc9f notes: refute 'any Canary change is a full reconfigure plus a full compile'
CONTAINER-NOTES said a Canary source change costs a full reconfigure against
/canary and a full compile, and told the reader to budget a session for it. It
was right about the defect -- the warm build tree at /sylph-home/re/canary-build
is configured with CMAKE_HOME_DIRECTORY=/work/xenia-canary, which does not exist
here -- and wrong about the consequence.

Symlinking /work/xenia-canary -> /canary restores the path the tree was
configured with. A one-file edit to command_processor.cc then rebuilt and
relinked xenia_canary in under 10 minutes at -j4, exit 0, no reconfigure and no
OOM. This is how the blend-state logging in the same session got built.

The symlink is untracked inside the repository, so the note also says to remove
it afterwards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 05:50:02 +00:00
sylph-decoder
e568a3494d tools: capture the BLEND STATE of every UI draw on the main menu
Canary's CaptureUiDrawForRE now logs RB_BLENDCONTROL0, RB_COLORCONTROL and
RB_COLOR_MASK per draw, raw and decoded, alongside the shader hashes and bound
texture it already logged. This script drives the game to the main menu and arms
it there.

Unlike menu_draw_capture.sh it does NOT pass --log_ui_draws: Canary's own source
records that arming is unconditional now and that launching with the flag
correlates with the title refusing (A), 0 of 7 runs against 4 of 5 without.

Committed before it is run, per METHOD.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 05:38:31 +00:00
sylph-decoder
dad9f2761a re: no per-element field on the disc separates the four too-dark frames
The port measures four elements as rendering too dark against the capture --
ptframe1/2 on the main menu, ptframe3/4 on EXTRAS -- with the shortfall
correlating +0.77/+0.80 with the BACKGROUND and only +0.24 with the element's
own contribution. That is the signature of a blend that scales what is already
there. It asked whether the disc selects one.

Three examples, one negative, wider than the one I gave last iteration:

* frame_alpha_census -- every T8aD sprite on builds 5 and 6 by alpha. It
  REFUTES the port's own sharpener: 'neither frame has a single fully-opaque
  pixel, against ptbase's 99.1 %' is true, and pteff10 (max alpha 130, 100 %
  partial, no opaque pixel) is measured by the port as NEARLY EXACT. So being
  wholly semi-transparent is not what makes the frames special.

* frame_vs_accurate_words -- all 12 T8aD header words for both screens, plus a
  per-BIT sweep of +0x04 and +0x08. NO word and NO bit puts the four frames on
  one side and pteff10 on the other. It also kills my own remaining candidate a
  second time: +0x08 = 0x8050 is shared with pteff21/22/23 on EXTRAS.

* frame_keyframe_unknowns -- the keyframe record's fade, tint, rotation and its
  two unexplained signed words. unknown_4 and unknown_8 are ZERO on every
  keyframe of both screens, so they carry nothing here; no frame takes a value
  of any field that another element does not.

Reach: the 60-byte declaration entry, the T8aD header word-wise and bit-wise,
and the keyframe record. Four elements, two screens. Not the executable's draw
path, which is the next commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 05:37:43 +00:00
sylph-decoder
a4b65e88db handoff: answer the blend-mode ask -- not on the disc, with the reach and one refuted candidate
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 05:13:52 +00:00
sylph-decoder
abab343d2e re: a .t32 element carries no blend/alpha mode -- undecodable, with reach
Answers the port's ask about ptframe1/ptframe2, whose residual is uniquely higher on
flat pixels than edges and signed one direction -- a body-intensity difference.

Prior work covers .prm primitives and a refuted T8aD +0x04 bit; neither covers a
.t32 element. All 15 words of the 60-byte declaration entry are read: 3 are the
name, 8 constant, the rest kind, focus index, position and pivot. The frames are
kind 0, identical to every other plain sprite.

One candidate found and refuted by myself: T8aD +0x08 is the only word where both
frames agree uniquely on that screen, at 0x8050 -- but 38 sprites carry it
disc-wide, only 8 named frame, and the high byte tracks the archive. It is an
atlas/format word, not a mode.

Also records a false positive of my own test: +0x00 and +0x08 first read as
'separating the frames' because those words are the name string.

So any blend the port picks is authored. Reach: not looked at the executable's draw
path, where a mode selected in code rather than data would live.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 05:13:27 +00:00
sylph-decoder
c06654293c re: name the elements under the port's hot residual tiles -- a frame, a button and an effect
sylpheed-port mapped the menu's edge residual at 64 px tiles and handed over
coordinates without names, which is the division I proposed: the map is theirs, the
element inventory is mine.

Under the hot band at x 384..704, y 64..256 sit ptframe1.t32, ptbtn01.rat (the NEW
GAME button) and pteff12.t32, an effect element -- all three hot under BOTH
coordinate readings, so the answer does not depend on whether their tiles are in
design or capture space. ptbtn02 is hot in design space only.

So the hot region is not one element but three of different kinds overlapping, which
is consistent with their null: they looked for two families of tile and found one
continuous population, so the region has no character of its own.

This names what is there, not what is wrong -- their map already excludes local
displacement in these tiles. Also records their control limit: a +2 px displacement
reads back +0.839 because the slope saturates, so any slope they report is a floor
on the displacement and never a ceiling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 05:02:19 +00:00
sylph-decoder
1e93b6a2e2 re: the menu's edge residual is not global misregistration and not blur
sylpheed-port ran the signed-edge discriminator I proposed but could not execute --
it needs their render beside the capture. Recorded as their measurement, with their
tool and commit.

Controls against deliberately damaged copies of their own render: a known +1 px
shift reads back as +0.938 px, a known blur as r -0.896 on the laplacian, neither
leaking into the other's channel. The menu against the capture gives -0.010 px
horizontal, -0.009 px vertical, laplacian r +0.103 -- and the weak blur term is the
opposite sign to the blur control.

So global misregistration and blur are both excluded, and of the three candidates I
named the misplaced soft element is the only one left.

Reach is the whole reach: a whole-frame fit excludes a global translation, not a
local one, since one misplaced element is a small share of 38752 edge pixels. The
next test is local and needs the residual map, which is theirs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:57:38 +00:00
sylph-decoder
1c85a9611c handoff: GP_DIALOG 2/3 are an EN/JP pair, measured on both locales
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:53:33 +00:00
sylph-decoder
93653dcb74 re: the tone-vs-geometry positive control rested on a number that cannot carry it
sylpheed-port found that my control's premise misreads their figure. The 0.06 %
counts pixels surviving -threshold 25%, differing by more than ~64 levels -- a
gross-displacement detector, blind to sub-pixel offsets and antialiasing, which are
exactly what a per-level LUT also cannot close. It says no gross displacement, not
geometry is right, and I read one as the other.

Their direct measurement, attributed and not reproduced here: after the LUT the
menu's residual is 6.94 on edge pixels against 2.20 on flat, with a known negative
leaving 0.00. So the menu carries spatial error and is not a geometry-free control.

The 32/68 result does not rest on it -- 'whatever a fitted per-level LUT cannot
close is not a per-level effect' is the method's own basis and is self-standing.
What the control shows is narrower than claimed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:53:33 +00:00
sylph-decoder
00172f5877 re: GP_DIALOG 2/3 ARE an English/Japanese pair -- measured, closing a withdrawn claim
The ja capture of DIFFICULTY is taken. EN and JP differ in 1.82 % of pixels, in four
bands and nowhere else: the heading (DIFFICULTY -> 難易度選択), a 2 px ring shift, the
BACK label (-> 戻る) and the footer. EASY/NORMAL/HARD do not appear in the differing
set -- the Japanese release leaves the three difficulty names in Latin script.

So 2/3 are a language pair, and the disc agrees quantitatively: 2.77 % of bytes
differ against 1.82 % of pixels on screen. This closes the item left open when I
withdrew 'an EN/JP pair' as a bare assertion, and it is now measured rather than
inferred from the disc's convention.

The JP screen also opens on NORMAL like the English one, and the sweep reproduced
the reset finding in Japanese.

Reach: one JP boot, one screen. It does not generalise -- GP_TITLE 4/7 is already
known to differ by more than text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:52:19 +00:00
sylph-decoder
df0d4f058d tools: the ja DIFFICULTY run uses --reach-only, which is why the first attempt failed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:41:41 +00:00
sylph-decoder
1e0d819108 tools: add --reach-only, so a caller that just needs the menu does not run the round trip
The ja DIFFICULTY capture failed because focus_persistence.py's round trip --
menu, B to title, A back -- did not return, leaving the game off-menu, and the
sweep that followed timed out with nothing to work with. Arriving at the menu is
the cheap part; the round trip is that probe's own experiment and is not every
caller's.

--reach-only stops once the menu is reached, and the session script passes it
through REACH_ONLY.

Committed before running.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:41:30 +00:00
sylph-decoder
f2e8d4f786 re: record the disc-side corroboration as a separate quantity, and the log-line substitution
sylpheed-port checked the JP layout from their export: the disc declares identical
button rows for EN and JP on main_menu and extras. Recorded separately rather than
merged with mine, because they are different quantities -- their rows are declared
rest positions, my 225.5 is a runtime ring row on a surface offset ~65 px from
design space. Either could have disagreed, which is what makes them independent
legs, after a week of finding legs that were not.

Neither is evidence for GP_DIALOG 2/3; both are about GP_TITLE.

Also records the mid-run substitution: I diagnosed a detector failure from a log
line showing glyph 11654 when the capture was one read away and showed the detector
working. A log line is a summary someone wrote, and reading it as the observation is
the same substitution as trusting a harness note.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:33:06 +00:00
sylph-decoder
2f5d25fb13 handoff: first JP main-menu capture -- initial focus 新規, layout identical, dialog question still open
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:31:11 +00:00
sylph-decoder
c7fdc55d12 re: JP DIFFICULTY not reached -- but the first JP main-menu capture, and initial focus is 新規
The run was launched to settle whether GP_DIALOG 2/3 are specifically EN and JP. It
did not reach DIFFICULTY: the reach probe's round trip failed at the final A, the
game sat off-menu at glyph 11654, and the sweep then timed out without pressing NEW
GAME.

Not a detector problem, which is what I assumed while watching. The JP menu detects
perfectly at glyph 320 against English's 327, both inside the 250..420 band; the
11654 is a later phase.

What it did establish: the locale took, and this is the first JP main-menu capture
in the corpus. JP initial focus is 新規 -- NEW GAME, top item, ring y 225.5, the same
item and row as six English boots. And the ring rows match English exactly, so the
JP build places its buttons where the English one does. That is language-pair
structure at the MENU, which is not the dialog pair the question is about.

Locale restored and verified at language = 1 by the trap, on a failing exit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:30:57 +00:00
sylph-decoder
6ee422b2d4 agents: the ancestor check prints nothing on success -- add the echo
sylpheed-port found this in their own §6 and it is in mine identically. I stated the
pass condition as 'exit 0' but left the command silent, so a reader running it as
written sees an empty line and cannot tell success from failure. Worse than a number
without a pass condition, because there is no number.

Now carries '; echo $?' and says why.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:12:36 +00:00
sylph-decoder
73bf90eb8d tools: a ja capture of DIFFICULTY, to settle whether GP_DIALOG 2/3 are a language pair
The byte comparison showed 2/3 differ in 2.77 % while sharing every element name --
what a language pair looks like -- but that they are ENGLISH and JAPANESE rested on
the disc's convention rather than on this screen. This captures it in ja.

Reuses submenu_focus_sweep.py with SWEEP_TARGETS=0 rather than a new probe, so the
JP capture is taken by the same navigation that produced the English one. The locale
is restored on ANY exit including a crash, and the probe never presses A inside a
submenu, so it cannot reach SELECT DATA and the guest throw.

Committed before running.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:11:47 +00:00
sylph-decoder
f427e4569d agents: give the merge-state commands their pass conditions, and run them as written
sylpheed-port's standard, applied here: a documented command nobody has executed is
the same class as a control that does not execute. Mine did execute, but they were
published without expected outputs, and a command with no pass condition is half a
check -- a reader gets a number and no way to know whether it is the right one.

Each command now carries its pass condition, the placeholders are real shell
variables rather than <branch>, and the last run's results are recorded: 330, exit
0, 0, 1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:10:19 +00:00
sylph-decoder
a9164aada3 agents: the merge-state counts go stale by construction -- say so and give the commands
sylpheed-port re-derived this the same day and read 329 for my branch where I had
written 328, because a commit landed between the measurement and the check. The
counts rise with every commit on either branch, so they are not the claim.

What does not move with them: main is an ancestor of both, the change sets touch
zero files in common, and the dry-run merge is clean. The document now says that and
carries the commands to re-derive all of it.

A number that goes stale the moment it is written, sitting in a document meant to
inform a decision, is the same shape this week has been full of -- and this one was
mine, one message after recording the class.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:08:09 +00:00
sylph-decoder
f7521df738 agents: measure the merge state instead of describing it -- both branches are clean
PROTOCOL says a human merges, and both agents have been blocked behind that while
describing it rather than measuring it.

Decoder branch is 328 ahead of main and 0 behind; port branch 257 ahead and 0
behind. main is an ancestor of both, so each is individually a fast-forward.

And they do not conflict with each other: from their merge base the two change sets
touch ZERO files in common, and git merge-tree --write-tree of the two heads exits
clean with no conflict markers. Both can be merged in either order with zero
conflicts.

Nothing was merged. merge-tree is read-only and no branch was modified -- this is a
measurement, and merging remains the human's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:06:04 +00:00
sylph-decoder
293040b61f re: verify that static records' declared cycles are visually inert in GP_TITLE
sylpheed-port turned my point -- that a static record still declares a cycle, so a
nonzero +0x08 against a largest time of 0 is a real disagreement -- into a check on
the screens they ship. Re-derived from my reader and it reproduces exactly: 65
nested records in GP_TITLE, 20 declaring a cycle with every pose at t == 0, and 0 of
those with any element carrying more than one pose.

A record whose elements each hold a single pose renders identically looped or held,
so holding them still is correct and now measured.

It includes ptbtn11/12/13, EXTRAS' buttons in both language entries, each declaring
120 units with one pose per element. Had any carried two poses, a menu button the
disc says animates would have been held still on the one submenu the port's P5 gate
walks.

Reach: GP_TITLE only; 1530 static records exist disc-wide against the 20 here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:03:32 +00:00
sylph-decoder
e2b7139273 re: settle the 49.6/92.3 split -- both are real, and the second explanation was wrong too
sylpheed-port corrected their own reconciliation and I reproduced it: 0 nested
records on this disc lack a timed keyframe. All 1530 are static -- timed, every pose
at t == 0 -- so the question is well-formed there and 'not exact' is a real answer,
not an absent one. A static record still declares a cycle length.

So the two percentages are two populations and neither corrects the other: 92.3 % of
animated records, 49.6 % of all nested records including static ones, same numerator
1643. Both need their population attached.

Two wrong explanations preceded this, both mine to carry: that my scan filtered
untimed records, which .max() returning Some(0) prevents, and then their 'questions
never asked' framing which I adopted.

The page is rewritten to the settled state rather than stacked. Nothing the port
depends on moved at any point.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 04:01:01 +00:00
sylph-decoder
e6a9d372f3 re: reconcile the 49.6/92.3 split -- same numerator, and my explanation of it was wrong
sylpheed-port reconciled the population gap exactly and I reproduced it: filtering
max_t > 0 gives 1781 records and 92.3 %, their figures precisely. Same numerator,
1643, both ways.

My stated explanation was wrong. I said the scan requires a timed keyframe; it does
not, because .max() returns Some(0) rather than None for records whose keyframes are
all at time 0, so 1530 records where the question has no content stayed in my
denominator and counted as failures by construction.

The conclusion is untouched -- +0x04 is 0 % under either denominator.

Records their diagnosis of why it stayed invisible: the numerator agreed to the
unit, so a shared 1643 read as agreement and neither of us looked there. And both
halves needed a qualifier neither carried -- 92.3 % is of the records where the
question is meaningful, not of nested records.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 03:57:21 +00:00
sylph-decoder
73c2b31e1b re: the +0x08 falsifier does not identify +0x08 -- the value is right, my argument was wrong
sylpheed-port aimed my own boundary finding at my loop-length control. Reproduced
from my reader over every pak: +0x04 has 0 violations too, so the falsifier rejects
+0x0c and accepts +0x04 and never discriminated. What identifies +0x08 is the
exactness statistic the page presents as secondary -- exact match in half the
records against 0 % for +0x04.

Population differs from theirs, 3311 records against 1781, because this scan takes
every pak and requires a timed keyframe, so the percentage moves but the
discrimination does not.

Second time this week with the weight on the wrong leg: a count taking credit for an
exclusion argument, now a falsifier taking credit for an exactness statistic, and
both times the real discriminator sat beside it described as a formality.

Their general form is sharper than my boundary rule: an interior consistency check
is satisfied by any internally consistent reading, and that is what a wrong offset
into a regular structure usually is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 03:52:09 +00:00
sylph-decoder
91767801c1 method: a record layout is only decidable at the table's boundaries
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 03:46:52 +00:00
sylph-decoder
2bc20ec31d tools: a layout control for structural claims -- and the obvious version does not work
sylpheed-port named a gap in their own rule: the fourth aside of mine to reach their
authored data was a STRUCTURE, not a decoration, and 'the unchecked things carry no
weight' did not cover it because a wrong field order looks like a fact. It carried
no weight only by luck. The fix belongs at my end, so this is the control that
should have existed when I published the layout.

The obvious form fails, and its failure is the useful part: checking that all
records are type-plausible passes on the SHIFTED alignments too, 69 of 70 in both
directions. A homogeneous repeated table has the same field types in sequence, so
any window starting on a field boundary type-checks and the interior carries no
information about phase.

Only the BOUNDARIES do. A shifted reading must consume a word from outside the table
at one end, and that word does not obey the field's type -- which is exactly how the
original error surfaced, record 0's handler reading as 0x10000000. Two-sided: the
published alignment survives at both edges and both shifts fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 03:46:52 +00:00
sylph-decoder
79704f360f handoff: correct the dialog record layout -- ids and names unaffected
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 03:43:35 +00:00