141cef4047144af44248f9d126f4e438cdf12533
29 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
141cef4047 |
port: wire flow.json's dwell, assert the three authored values the port hardcodes
Applying the prior from six prior findings to authored/ itself: five keys had no reader. dwell, ramp, left_right, input_during_transition, stems. dwell is the one that mattered. Its own text says a measured hold goes there and a number placed there did nothing -- and two iterations ago I asked the Decoder for measurements destined for that slot. Wired now, and it stays EMPTY: the splash dwells are declared on the disc and measured to agree. I wired it to the wrong branch first and it did nothing, silently -- holding longer after settle is absorbed because the screen still leaves at exit_time + black_hold. A dwell must delay the departure. Caught only by testing the control: +120 units moves the transition 4.46 -> 6.43 s. ramp, left_right and input_during_transition describe hardcoded behaviour and are written like switches. Rather than invent the missing implementations, they are now asserted against the value the port was built for, naming the file -- which is the distinction left_right's own why claims to make and was not making. The validator is called, not merely defined. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
2fe1f06599 |
port: --focus was ignored on the menu path; focus rendering now verified against the oracle
live-main-menu-options-focused.png -- the only capture of a known focus state -- was untestable because --focus= parsed, was stored, and was overwritten by the authored initial focus on every _menu_enter. Every run logged focus ptbtn01 whatever was asked for. Now pushed into the menu model so navigation continues from where it was forced. With it working, each capture picks out exactly one button: ptbtn04 at 0.1355% against 0.70-0.82% for the others on the OPTIONS capture, and ptbtn01 at 0.0705% against 0.72-0.84% on the plain one. 5x and 10x discrimination. First time the port's focus rendering has been checked against the game at all -- the existing main_menu row uses an authored focus and could never have caught a focus error. Records in flow.json that live-main-menu.png shows NEW GAME focused, so the authored initial_focus matches the one frame it can be checked against -- and that this does NOT overturn Q5's measured instability. It stays authored. Adds main_menu_options to verify-capture at 0.13%. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
a170e1fbc6 |
port: a second capture closes the sweep-geometry question; title+plate matches at 0.00093%
live-title-press-a.png was unused in the corpus. Posed at t=237 -- inside the plate's 8-unit window -- the port matches it at 0.00093%, against 0.0124% for the no-plate capture at leaf phase ~400. Two captures, two different phases, both under 0.013%: a systematic sweep-geometry error would leave a floor in both, so last iteration's caveat is closed. Sweeping the whole screen's instant against capture 1 gives at best 0.148% at t=230 -- 10x worse than the leaf-only fit. So that capture is the screen SETTLED with the sweeps still looping, which is the first independent evidence for the authored loop_leaf decision. Fixes the cause of a flat 1% floor: --screen=X --overlay=Y pushed the raw elapsed clock into the overlay (9 units at capture), so press_start drew nothing -- the flag whose purpose is 'put the plate on the title'. A static overlay now poses at its own arrival; the --boot shared clock is untouched. Adds title_plate to verify-capture at 0.00%, the most sensitive row in it. Its instant is FITTED and labelled as such. Also records that I nearly committed a wrong cause for the overlay bug: I wrote that nothing drives the overlay's clock outside a sequence. It is driven, every frame, from view.time_units. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
9610662b82 |
port: add --leaf-time, and the title's residual is the sweep phase (~400 units, not 357.7)
The Decoder's refined sweep fit had never been testable: verify-capture passed it as a whole-screen --time that pose_at discarded, and asking for it honestly poses past the title's group end. --leaf-time separates the leaf's clock from the screen's. Controls: the renderer is deterministic (3 runs bit-identical) and the sweeps move 0.40% of the frame between phases, so the comparison can see them. Sweeping the full 600-unit span gives a sharp basin at 390-415 units (0.0124%) against 0.2532% at t=357.7 -- 20x. So the title's 0.21% residual is the sweep phase, not structure: at the fitted phase it matches the capture as well as the splashes do. NOT adopted: the port loops the leaf freely and re-posing the harness to the fitted value would be tuning until they match. Filed instead, with the question of whether 357.7 and this are even the same quantity. Also verified last iteration's settle-window change was surgical: only press_start and its twin moved, 14 screens unchanged including title's Decoder-confirmed [160,236]. Settle-window ties exist on 4 screens but all sit under the 30-unit bar, so the arbitrary tie-break never reaches the runtime. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
29a5b4a394 |
port: the PRESS (A) plate could not be drawn at any instant -- four faults, and a misquoted number
1. --time= was silently ignored on any screen with a settle window >= 30 units: pose_at overwrote the requested instant with settle_instant. ScreenView.frozen now marks an explicit instant and skips both clamps. 2. press_start's settle window was [0,214] -- the dead stretch BEFORE the plate exists -- so its settle instant was t=107, where the element is alpha 0. The exporter now rejects intervals in which nothing is visible. title keeps [160,236], the interval the Decoder's draw stream confirmed. 3. My authored looping_focus_records entry for press_start/ptbtn00 drew a dim focus record INSTEAD of the plate's own sprite: max 0 vs max 252.5. Deleted -- an authored guess that overrides a decode with a worse answer is removed. 4. verify-capture passed --time=5.9617 for the title and it was never applied. Every title figure it has printed, including the 0.26% quoted to the Decoder, was measured at the settle instant under a note claiming t=357.7. Both rows now pose by omission and the note matches. title is 0.21% honestly; splashes unchanged at 0.01%. The boot's end artifact now contains the plate (region mean 95.7 vs 33.6). Corrects last iteration's BLOCKED row, which had the entry's effect backwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
921dec9725 |
port: fix two capture bugs, and the second was hiding a missing PRESS (A) plate
1. --capture with --script photographed before the first press (t=0.133 s, 10 of 16 elements transparent). Two runs differing by two presses came out bit-identical and I read it as 'runtime focus never changes'. Deferred to the end of the script; verified max 235 and t=82 units. 2. --boot --capture= wrote NO FILE: _finish_boot() is reachable only from the overlay-quit branch, but line 412 quit first because _overlay_spec is cleared when the overlay is raised. Pre-existing, confirmed by stashing. Fixed by also requiring _overlay_quit_at < 0.0. 3. The artifact that now exists shows the boot's end frame is bit-identical to the title alone -- no plate. ptbtn00 is opaque for 8 units (236-244) and the boot captures at 246.54, because it waits for build 4 to finish fading at t=261. Both halves of that are sound and they are incompatible. NOT changed; filed, since what settles it is what the game does after t=244. Defect 3 was invisible while defect 2 existed: a capture flag that writes nothing cannot show a missing element. Also records that runtime focus is FINE -- my contrary reading came from 410 files whose names did not match the flag I passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
e5398be93b |
port: add wait:<seconds>, observe the bed's loop, and measure the seam at 3.4 s of silence
The port could not be asked to run for a stated duration -- a bare step is a no-op that returns at settle -- so nothing after the settle point was observable. An 87.7 s bed on a harness whose longest menu run was 7 s. The bed loops at 87.8 s against the track's 87.7 (r=0.947 and 0.885 on a clean bed-only recording): loop: restart behaves exactly as authored. First end-to-end observation of P6 looping. The authored 'audibly wrong at the seam' is confirmed and quantified: 36 consecutive near-silent 50 ms windows, 84.40-87.80 s, about 3.4 s of silence after a fade from RMS 2057 to 431. Recorded in authored/audio.json. It does NOT license trimming, which would still invent a loop point. My first wait: used create_timer and ran 39% long (30 s requested, 41.7 s wall) because an idle scene throttles the delta it counts down on. Now polls Time.get_ticks_msec: +4.6%. Checked before generalising: over a boot the port's clock tracks wall clock within 4%, so animation timing is sound and the earlier splash-dwell agreement stands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
b71c35acf4 |
port: the menu bed plays under the cutscene -- announced, deliberately not fixed
MenuAudio.stop_bed() exists and is called from nowhere, so the bed started on the main menu runs through S00A and loops on past it, putting two unrelated music tracks on the bus at once. Established from the source and authored data, not from measurement. NOT silenced: MISSION says leave an unmeasured detail plainly wrong rather than plausibly invented, and music over a cutscene is caught by any listener in a second where ducking would sound right and be a guess. _play_video announces it instead, and stop_bed is kept as the one line to change. Also records that the envelope correlator is unreliable for music under music -- 0.15-0.42 for every candidate, peaks moving with window and template. I was drafting '46 s of unexplained audio' when the cause was the authored loop: restart. A margin needs a control at the SAME SNR. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
e6fc2977ff |
port: make ScreenView say what it could not draw; refute 'first-declared paints first'
skipped[] has been tracked and read by nobody since P1, under a comment saying a silently missing element looks like art. _note_structural prints from inside ScreenView rather than returning a value for a caller -- routing it through a caller is exactly what did not happen. Structural skips only; transparent-at- rest is ordinary animation. Zero found today: a guard, not a fix. The first version of that scan was a FALSE PASS: screen_view.gd did not parse (a line inserted at three tabs inside a four-tab block -- the substring assert matched a shallower indent), so grep counted zero from a dead script. The scan now counts the summary line as a positive control. Refutes 'the first-declared element paints first', which would have made the forced-backdrop rule redundant since all six forced elements are index 0. False on 8 of 16 screens -- decisively on main_menu, where index 0 is pteff00, painted LAST, and pteff00 is a measured control. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
77292e98fc |
port: withdraw 'the boot is known too fast' -- the splash dwells are declared and the port already played them
The Decoder measured both splashes over 3 cold boots: publisher t=0..255, developer t=0..210, the developer agreeing with wall clock to 1.1%. The port emits each declared value plus the 9-unit black hold, exactly. No code change. My error was the generalisation, not the arithmetic: build 4 is the title, whose exit is caused from outside its timeline, so it holds; a splash's exit is caused by nothing, so it plays out. I used the one boot screen the port is unaffected by to overturn the two it governs. Declining to scale by 9x while adopting the conclusion that implied was half a caution. Also refutes their two splash boundaries as not comparably anchored: 2.237 vs 2.414 units/frame in one boot, and the publisher has a glow symmetric with the developer's three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
2e29572b02 |
port: the clock freezes at settle -- my window is the game's, and the boot is known too fast
The Decoder measured build 4's top-level clock stopping inside [160,236]. The exporter computes title's settle window as [160,236,198] from the file alone. Same interval, two independent methods -- the first evidence for the settle instant that does not come from our own renderer. ptcopyright reaching alpha 255 exactly at t=160 agrees from a third direction. Corrects a claim in three places: timing.json, flow.json and boot.gd all said a screen's dwell IS its keyframe group and the port reproduced 'the disc's own pacing'. Build 4 declares ~120 presented frames and dwelled ~1100. The decision to hold zero extra stands; the claim that it was faithful does not. Checks their two declared spans against the file: both exact, with a 106-vs-105 interval-convention quibble that changes nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
3e578882c2 |
port: the title's sweeps loop, the black hold is 9 units, and one claim refuted
THREE THINGS FROM THE DECODER, one of which I am not taking. REFUTED: "the developer splash is one composited quad, the bounding box of the three logos". The observed quad is 525x259 at (378,155). The three logos' bounding box is 500x421 at (390,164) -- a 259-tall quad CANNOT contain them, and palogo_anima alone starts at y=449, thirty-five pixels below that quad's bottom edge. The observed quad matches the union of gamearts_eff and seta_eff, 521x261 at (379,154), to about four pixels in every dimension -- and both of those are TRANSIENTS my own census flagged, dark by t=45, so a frame containing that quad is a build-in frame rather than the settled screen. I cannot see their draw stream, so I sent the arithmetic rather than a verdict, and the port keeps drawing three: I will not stop drawing an element on a claim whose stated identification excludes that element from its own bounding box. THE BLACK HOLD IS 9 UNITS, NOT 12. I authored 12 from Q7's luminance plateau of 0.17-0.23 s, supported by the menus' transition quad. The Decoder counted SUBMITTED QUADS instead -- luminance cannot separate the outgoing fade's tail from true black. Four frames with no sprite quad at all, at 2.284 units/frame derived from the disc as its own clock, gives 9.1 units = 0.152 s (6.9-11.4). That overlaps the luminance figure only at the top, and the true black is SHORTER still since both boundary frames carry picture. My 12 was supported by analogy -- a different screen's quad on a different path -- and a number that fits by analogy loses to one measured in place. verify-dwell's bound moved with it; both screens still agree. THE TITLE'S SWEEPS LOOP. The oracle shows the quad oscillating over its whole x range and resetting hard, one reset in the first title dwell and two in the second. The loop-length field could NOT have settled it, correcting a hope I had stated: both records declare exactly their last keyframe time, slack zero, and "loops at 600" and "runs once for 600 and stops" write the identical header. Verified on the two sweeps' LCM, since their periods differ: 600 and 720 realign at 3600 units, mean diff 0, against 0.438 at half that. Scoped to the title. The menus declare the same lengths but the oracle measurement is of the title, and my own weak evidence points the other way there -- best match with the sweeps off-screen, three times worse mid-screen, against a 73% on-screen duty cycle if they looped. Two weak signals in opposite directions is a reason to scope, not to pick. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
7230fa846e |
port: the publisher residual was a missing black hold -- we had both dismissed it
I carried this as "0.03 s outside a composite bound, probably a property of the bound rather than the game", and the Decoder agreed. Both wrong, and the way it was settled is the point: I stopped reasoning about the bound and filmed the transition. At 0.05 s the port fell straight out of the publisher's fade into the developer logos -- mean 5.06 -> 0.32 at t=4.20, then 5.65 at t=4.25. NO BLACK FRAME AT ALL, where the oracle measures a 0.17-0.23 s pure-black plateau (HANDOFF Q7). The bound was fine; the port was missing a fifth of a second of black, and had been since P3. Authored at 12 units because the boot path has nothing to read it from: publisher_logo and developer_logos each carry a single palogo_eff0, a 1280x720 primitive with ONE keyframe at t=0 -- static, not a transition ramp. The menus' quad declares black for 12 units and 12/60 = 0.200 s sits mid-range, so the number is the disc's where a screen has one. Filmed after: t=4.25, 4.30, 4.35, 4.40 all at mean 0, then the developer logos at 4.45. publisher interval 4.26 DIFFERS -> 4.47 agrees; developer 3.62 -> 3.73, still agrees. Settled-frame comparisons untouched, as they should be. THE LESSON IS THE SHAPE OF THE DISMISSAL, NOT THE NUMBER. "A 0.03 s miss against a bound composed from two measured ranges plus jitter slack is more likely a property of the bound" is plausible, was accepted by both of us, and was wrong. The composite bound is why the miss looked small -- the underlying gap was 0.2 s -- and a plausible explanation for a small number is how a real defect stays hidden. The film cost one command. Also recorded: the Decoder has reproduced across two build-ins that the console NEVER draws ptlogo_back2eff3 (0 draws against ~5 expected), with sampling phase, invisible draws and position error all ruled out -- but WHY is not established, and nothing in eff3's record differs from its neighbours. The port keeps drawing it, deliberately: dropping an element the disc declares on a measurement with no mechanism is authoring a behaviour neither agent can derive, and nothing this port gates on would notice, since the flashes live only in the build-in and verify-capture compares the settled frame. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
fb472e8dfc |
port: pose a settled screen at ONE instant -- title 1.81% -> 0.26%, splashes to 0.01%
The Decoder's finding, applied. `rest()` returns each element's last hold keyframe chosen independently of every other element: right for anything that ends the screen settled, exactly wrong for a transient. The title's ptlogo_back2eff1 is a two-frame flash (0 until t52, 255 at t54-56, 0 by t58), so its last hold IS the flash peak and rest() left it burning -- five of them, drawn at once. The settled instant is the longest interval containing no keyframe time, over a bundle's TOP-LEVEL elements. Reproduced here before adopting: title [160, 236], midpoint 198, the Decoder's number to the unit. The top-level restriction is what makes it match -- including the ptloop leaves gives [269, 540]. AGAINST THE ORACLE: title 20.92 RMSE 1.81% -> 14.61 RMSE 0.26% publisher_logo 9.05 0.75% -> 2.17 0.01% developer_logos 8.86 0.33% -> 3.05 0.01% main_menu 0.08% -> 0.08% window too narrow extras 0.19% -> 0.19% window too narrow Seven times fewer differing pixels on the title, seventy-five times fewer on the publisher splash, whose differing region is now a 13x18 box. The largest correctness gain this port has had, and none of it is mine -- it is a decode computed from the keyframe table with no reference to any capture. APPLIED ONLY WHERE THE WINDOW IS WIDE, and the bar is not invented. This export's widths split with nothing in between: 214, 190, 145, 76, then 12, 12, 8, 4. The bar is 30 units -- the Decoder's disc-wide census puts the knee there (30% of bundles >= 30, 42% under 10, the latter mostly loop* fragments meant to be in motion) and this export's screens sit 4x either side with nothing between 12 and 46. Two independent populations agreeing on where to cut. Checked unbroken: boot pacing unmoved, scripted walk runs end to end with focus restored. Also recorded: my "34 focus-record elements, only 2 varying" is right for GP_TITLE and reads as a fact about the format -- disc-wide it is 210 varying, 202 with rest() at the peak, concentrated in the paks a wider port reaches next. And their sharper framing, which I have adopted: a pulsing element has no resting pose at all, so rest() is MALFORMED rather than mis-answered on one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
521b0b1f85 |
port: the focus ring had silently stopped, and the plate's period is now the disc's
BLOCKED said the record-layout change touches five things: pose_at, settle_units, spin_period_units, exit_ramp_units and the plate. I checked two, reported that, and did not work the rest of the list. `spin_period_units` required "the first timed and the second untimed". Under the corrected layout the ring reads t=0 rot=0 and t=120 rot=360 -- both timed -- so the rule returned 0 and THE FOCUS RING STOPPED SPINNING. Nothing reported it: a period of 0 is a legal "this element does not spin". Rewritten to take the SPAN between the two poses: 120 - 0 = 120 units, the same number the old rule produced, which is evidence the corrected layout is self-consistent rather than merely different. Verified the way P5 verified it, by bit-identity one period apart on the ring's own 60x60 box so the ptloop sweeps cannot confound it: 0 at +120 units (twice), 8.61 at a quarter period, 8.88 at half. Three wrong instruments on the way, and the sequence is the lesson. A whole-frame `max` saturates on one rotating edge (adjacent frames scored 131 with a mean of 0.022). A live --menu filmstrip jitters by up to a frame, which is ~3 degrees of ring. And a whole-frame comparison is dominated by the sweeps, which move 480 px over one ring period. `--focus=<id>` was added so a --screen run can draw a focus record deterministically, which is what made the check reproducible. THE PLATE'S PERIOD IS NOW 105, THE DISC'S OWN GROUP LENGTH, and it disagrees with the measurement. The ambiguity the entry carried is gone -- it used to say the cycle might restart at t=6 rather than 0 and that nothing separated them; the group now runs t=0 to t=105, both at alpha 0, and there is one reading. But 105 units is 1.750 s, or 1.906 s scaled by the factor the ring shows between its declared 120 and its measured 2.177 s -- about 17% below all four corpus timings (2.12 / 2.19 / 2.34 / 2.31). The old 129 gave 2.34 s, at the top of the range, which is why it looked right. 129 was the last timed keyframe plus exit_ramp_units, and that constant is deleted. A period built on a constant that no longer exists cannot stay even though it fitted better, so the port ships the disc's number and says it is wrong. Verified bit-identical 105 units apart, 0.83 at 30 units. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
ace071dd7b |
port: delete exit_ramp_units, invert the format's own rule, and guard a scale-0 leaf
FOUR THINGS, and the first is what MISSION section 3 calls the measure of
progress.
DELETED `exit_ramp_units` AND `exit_ramp_seconds`. They were authored because the
disc had no time slot on a group's final keyframe, so the ramp into it was the
one unknown duration per screen. Under the corrected record layout that keyframe
does not exist -- a group is an 8-byte header then frames x {u32 time; 36-byte
pose} and every pose is timed. VERIFIED DEAD BEFORE DELETING: setting it to 9999
(166 s) moved the boot's transitions by 0.04 s, which is wall-clock jitter, and
both uses in ScreenView are gated on a condition that no longer fires on any of
the export's 866 keyframes.
INVERTED THE FORMAT'S OWN RULE. `check.rs` enforced "the final keyframe has no
`t`; the disc has no time slot there" and FORMAT.md stated it. Both are now
backwards, and the validator fired 150 times on a re-export. I had not run
`check` between pinning the tag and measuring against the oracle -- the pixel
harness was green while the format validator was failing on every screen with a
multi-keyframe group. A correctness harness does not replace a format one; they
fail at different layers.
GUARDED A SCALE-0 LEAF, which the Decoder hit in its own renderer: its leaf
branch marked the element drawn unconditionally while the blit returned early on
zero scale, so a scale-0 leaf suppressed its parent and blanked the element --
live on all four loading screens. This port did not have the bug only because
authored/rendering.json happens not to list pgloading_loop5. That is an accident
of a gate written for another reason, not a defence, so `_draw_leaf` now reports
whether it drew and `_draw` falls back to the parent.
ISOLATED THE PACING QUESTION rather than leaving it as a suspected regression.
Legacy association: publisher 4.70 agrees, developer 3.92 DIFFERS. Corrected:
publisher 4.26 DIFFERS, developer 3.62 agrees. Both misses are ~0.03 s outside a
composite bound. The association traded which screen is marginally out; it did
not regress the pacing.
Bumped the pin c -> d for the parser and audio changes. Its headline renderer
change does not reach this port: sylpheed-cli builds from the workspace crate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
|
||
|
|
cb616ab9af |
port: implement the decoded leaf composition -- and it does not close the 1.82%
The Decoder decoded the rule I refused to guess: draw the leaf on its own timeline, do NOT multiply the parent's alpha in. Multiplying is refuted rather than unsupported -- at the fitted time the parent has expired, so leaf x parent predicts zero for both quads and the sweeps would be invisible. They are drawn. Implemented: `_draw_leaf` runs the leaf unclamped, like the spinning ring and for the same reason -- held at its own rest.t the leaf sits at x=1521, entirely off the right edge, so `holding` would delete the sweeps rather than settle them. AND IT CHANGES NOTHING MEASURABLE. The title is still 1.82% against the oracle: 1.82 at t=261, 1.81 at t=355, 1.79 at t=420. At t=355 my interpolation puts the leaf's top-left at x ~ -324, off-screen left, where the Decoder's model puts the quad's CENTRE at 981. Those cannot both be right, and it is not something to tune away -- it is a disagreement about how the leaf's keyframes become a placed quad, most likely in the pivot and the rotation about it. Handed back with both numbers. So: the exporter no longer drops the data, the composition rule is implemented as decoded, and the port's largest oracle gap is exactly where it was. Fixing the export was necessary and not sufficient. TWO FLAGGED ELEMENTS DELIBERATELY NOT DRAWN, in authored/rendering.json with reasons. title_jp/ptlogo_eff2 (parent 125%, leaf 100%) is the same shape and is the element DECISIONS has recorded since P1 as the largest render disagreement -- but the Decoder said plainly "I have not tested it", and drawing it would extend a decode past the case it was fitted on. pgloading_loop5's leaf is scale (0,0), and scale-0 is one of the three historical failures this corpus names. Neither can be adjudicated here: title_jp has no oracle capture, and verify-screen compares against a renderer that draws no leaves at all, so ANY leaf drawing increases that divergence whether right or wrong. Its max went 155 -> 232 when they were drawn, and that number is not evidence in either direction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
c1edd13848 |
port: the title residual is a horizontal redistribution, and three of my own explanations are dead
`title` is the port's largest disagreement with the oracle at 1.82%, and last iteration I attributed it to the moving ptloop sweeps without checking. Wrong, and so were the two hypotheses I formed after it. NOT THE SWEEPS. ptloop01/02 are 399x180 at (441,270) -- small and central -- and their exported keyframes hold pos, scale and rotation constant. The difference peaks at x~1088. NOT AN OVER-HELD ELEMENT. Added `--no-hold` to render the alternative: playing the title's groups past rest fades the screen to black by t=5.2 s, 30.97% differing against 1.82% held. Holding at rest is right. NOT A TIMING OFFSET. Sweeping the build-in gives 24.05% at t=1.6 falling monotonically to 1.68% at t=4.18 and 1.82% settled. The capture is at the settled end. WHAT IT IS: a horizontal redistribution. Signed difference by cell shows the port DARKER centre-left (-13.1, -8.3, -6.6) and BRIGHTER right (+16.0, +9.9), nearly cancelling -- whole-frame means 63.8 against 62.5. Brightness in the wrong place, not a level error or a tone ramp. It falls in the rows spanned by the two wide elements ptlogo_back2 (1118x262) and ptlogo_back2eff (1133x280), with the column profile falling off past x~1152 against their right edges at 1189 and 1197. AND THE EXPORT CARRIES NO BLEND MODE. ptlogo_back2eff's keys are declared, id, index, keyframes, kind_raw, layer, layer_source, pivot, rest, role, sprite -- there is no blend field, in this element or in FORMAT.md at all, and the port composites everything with normal alpha. If the game draws `_eff` layers additively, a wide gradient sprite would produce exactly this signature and nothing in the export would reveal it. Asked, not assumed; I have not tested it, and I am recording it because the three I could test are dead. Recorded and NOT acted on: pteff02's rest.t is 46, where its fade is 25% black, while its own group reaches 0x00000000 at t=236 -- so the port holds a black veil the timeline removes. Third instance of rest.t naming a hold that is not the settled state. It does not explain the residual: removing a darkening veil would make the port brighter still, and it is already brighter where it disagrees. `--no-hold`'s first version set the flag thirty lines before `view` exists and silently rendered nothing, caught because the loop found no files rather than because anything reported an error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
af1d69ed4b |
port: the PRESS (A) plate pulses -- authored per element, because 82 of 212 share its shape
The human listed pulsation as first-class and the port drew nothing: the plate's focus record `ptbtn00f` was never reached, because press_start has no `buttons` and nothing is focused. That it LOOPS is measured -- the corpus timed the period four times (2.12 / 2.19 / 2.34 / 2.31 s) and you cannot measure a period unless the thing repeats. THE RULE I WAS GOING TO WRITE DIED IN THE CENSUS. The spinning ring is a rule in the renderer because it has a disc-wide check: 16 of 212 elements match its shape and all 16 are focus rings. The analogous shape for a pulse -- keyframes varying only in alpha, first alpha equal to last -- matches 82 OF 212, including ptcopyright, palogo_sqex, ptmsg and every _eff fade. A renderer rule on it would make the copyright notice pulse. Narrowed to focus records it matches exactly one distinct element, and a rule justified by n=1 is a special case wearing a rule's clothes. So it is a LOOKUP in authored/timing.json keyed <screen>/<element>, with the census recorded beside it so nobody widens it later. The period is 129 units -- the element's own group under the port's existing model: last timed keyframe t=105 plus the authored exit_ramp_units of 24. No new constant. 2.150 s at 60 units/s, 2.295 s at the ~28.1 fps the emulator presents, against measurements of 2.12-2.34. IT IS A CHOICE AND THE ALTERNATIVE IS STATED: restarting at the group's first keyframe (t=6) instead of 0 gives 123 units = 2.050 / 2.189 s, also inside the measured spread. Nothing separates them. t=0 is taken because it is where every other group starts -- consistency, not evidence. Verified the way the ring was, by bit-identity one period apart. 20 periods is 43.00 s = exactly 172 film frames: frames N and N+172 differ by 0-1/255, while the control a quarter-second off (43.25 s) differs by 58.7/255. On the held boot title the glow-box mean swings 26.0 <-> 37.7. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
3aa74ea029 |
port: the intro had no dialogue because the voice is a separate asset, and I concatenated it wrongly first
A human play-test heard music under the boot intro and no voices. The obvious reading -- the 5.1 fold dropped the centre channel -- is wrong. `ADV.wmv` carries music and effects only; a cutscene's voice is a separate continuous XMA stream in `sound.pak`, bound to the movie by the manifest in `tables.pak`. Nothing was dropped. The exporter had never been asked for it, so every fidelity measurement in AUDIO-VERIFICATION.md would have come back clean. `audio::export_voice` resolves it with `media::resolve_movie_voice_region` and never by filename: `RT01A`'s voice lives inside `VOICE_ADV.slb`, so a name match is correct on exactly the two movies this port would have spot-checked. Decoded, not authored -- so it runs outside the `authored/audio.json` block. THE FIRST VERSION CONCATENATED THE REGION'S CHUNKS AND WAS WRONG. It produced 359 s of dialogue for a 137 s movie. Decoding and timing each chunk shows two of them equal to six decimals and each spanning the whole movie -- HANDOFF Q10's decoded two-stem shape on a second asset kind -- so they are summed at 1/n. The error was visible only because the first version recorded the decoded length against the movie's instead of clamping to it; the clamp `media`'s own doc comment invites, and which `sylpheed-viewer` applies, would have produced a file of exactly the right duration containing the wrong audio. The dropped leading chunk matches no duration in its region and is NOT closed here. It is the same signature as `BGM_103`'s third sub-wave, already open in BLOCKED.md, now corroborated on an independent asset kind. Raised with the Decoder; the manifest names every chunk dropped and its length. Also in this commit, and separable: * `--skip-at=SECONDS` -- `--script` structurally cannot press during a movie, because `_script_settled` waits while `_player != null`. That is why "does (A) skip the intro" had been read out of the source rather than measured. * MISSION section 6 pins a 5.1->stereo matrix and this exporter has shipped a different one since P4 -- the same weighting, 7.65 dB quieter -- and said so nowhere. Re-measured with the right instrument (float decode, whole file, count the samples that would clamp, not a peak reading): the pinned matrix puts ADV at +4.26 dBFS on 4406 samples, while S00A never clips. So the pin overloads one movie and the constant is over-broad for the other. NOT changed -- the level of a mix is what section 6 reserves to a human. The export now carries a warning with the numbers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
b37ad246ff |
port: implement MODDING rule 4, and withdraw a red flag that was my own bad measurement
MODDING.md calls base-and-overrides "a design constraint on the exporter today, not a milestone to add later". Nothing read `data/mods/` at all -- the directory has existed since the monorepo merge with a .gitkeep and no code path anywhere. Eight milestones shipped past it. ExportTree.resolve() now shadows by path, and every read goes through it: screens, sprites, cues, the music bed, movies. MenuAudio was reading tree.root directly and would otherwise have made audio the one asset kind a mod could not touch, for no reason a modder could have guessed. No manifest, no registration step -- the path IS the registration, which is the whole of the rule. One tree, not a stack: layering needs a load order and nobody has asked for one, so data/mods/README.md says that rather than inventing it. Every shadowed file is printed as it is read. The first version summarised in _ready, before any asset had been read, so it always said "nothing shadowed yet" -- a report structurally incapable of reporting anything, which is worse than none because it looks like an answer. data/mods/ was NOT gitignored, and that is a hole in a hard rule: a mod is usually an edited game asset, and this was the one directory a user is invited to put modified sprites in and git would have taken them. Now excluded except the README. Gate: a synthetic 203x43 magenta PNG (nothing disc-derived) at data/mods/sprites/title/main_menu/ptbtn01.png changes 8501 pixels in a bounding box of exactly 203x43 at the button's position, and `check` still passes. RAISED, NOT RESOLVED: MODDING.md says the tree is data/base/, PORT-MISSION.md §3 and the exporter and .gitignore say export/. Both are mission files and only the human changes a mission. REFUTATION on Q3's paint-order key: 2 of 16 screens did not match a stable sort by layer key -- but that was my test. pgloading_eff00.prm carries NO layer key (layer_source "none"): a primitive with no sprite header and no implied-name fallback. I sorted keyless first; the decoders put it last, which is right, since it is the full-screen black quad and HANDOFF's own sentence is that the fade quad paints last. Completing the rule to "keyless last" gives 16 of 16. SURVIVES. Recorded because the published claim does not say where a keyless element goes and there is one in the archive. Separately the tie-break's reach looks understated: 105 elements share a layer key across 12 of 16 screens, where HANDOFF characterises the cost as "one element's blend on one screen". WITHDRAWN, and it was mine: I filed "the runtime mix has no headroom" in red twice, off a peak reading. Measured properly it is 43 samples at full scale in 5.9 s and 24 in 98.5 s, longest run 0.25 ms -- the disc's own confirm cue on a transient, possibly only in the 16-bit save. Nothing changed, deliberately: attenuating would be an unmeasured level decision of the kind I refused for the loop point. A peak reading is not a clipping measurement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WM5XL4HfrHuxz8RiMWdCMC |
||
|
|
7a6a027fc8 |
port: P7 -- the new-game intro plays, and the two screens it skips are named out loud
S00A has been exported since P4; what P7 needed was something to play it and a
defined place to land. Both are here, and the interesting part is the gap.
The real chain is NEW GAME -> DIFFICULTY -> SELECT DATA -> (A) on a save slot ->
~4.5 s -> S00A. DIFFICULTY and SELECT DATA are MEASURED destinations that are not
GP_TITLE builds, so no screen file exists to go to. The port jumps from NEW GAME
to the one thing in that chain it has -- and the whole design is about not
letting that read as a sequence:
* MenuFlow.accept returns a new kind, `video`, rather than folding this into
`blocked`, because the caller has to announce the skip and a distinct kind is
what forces it to;
* the runtime prints the skipped screens by name on every run;
* flow.json carries `skipped_chain` as DATA, so what is missing lives beside
the decision instead of inside a GDScript string.
After the movie the port returns to the title. Authored, and it has to be: the
game goes into mission 1 and gameplay is out of scope. The ~4.5 s before the
movie is left EMPTY on purpose -- GP_TITLE does carry a loading screen and 4.5 s
is about the right shape for one, which is exactly why that belongs in BLOCKED.md
and not in flow.json.
`--script`'s 20 s per-step timeout would have killed every movie run at step 1.
Raising the constant would have been wrong the other way: a movie stuck at frame
0 would then hang the job, and a job that waits is worse than one that fails. The
test is now LIVENESS -- while get_stream_position() advances the deadline moves
with it, and a stalled movie still trips the same 20 s.
Found while looking: GP_TITLE's four unnamed builds (entries 0, 1, 12, 15) are
LOADING screens -- every element in all four is pgloading_*, and LOADING is one of
the three names the decoder read out of the title part's state function. NOT
renamed here: which member of each pair is which locale is an inference, and a
name stops being questioned once written. Handed over.
One of them is a second casualty of the rest.t problem, and a worse one:
pgloading_eff00.prm rests OPAQUE BLACK at t=38, so anything drawing that screen
at its declared rest paints a black rectangle over all of it. The title's case
only dimmed a frame.
REFUTATION, attempted and SURVIVED: HANDOFF says "exactly the six screen builds
carry the black .prm quad while the six overlays do not". Counting bundles with a
full-screen black primitive gives 8 and 4 -- build_12/15 carry one too. But
theirs runs black -> held -> clear where the transition quad runs black -> clear
-> black, so read strictly as "the quad whose group is the transition" the claim
holds. Recorded anyway: there are two kinds, and the naive census over-counts.
Gate: NEW GAME -> S00A plays 93.75 s against a declared 93.9 -> title, with
98.453 s recorded off the Master bus. What that does NOT show is that S00A's own
audio is in the mix -- bed and movie were not separated in this run, and the
write-up says so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WM5XL4HfrHuxz8RiMWdCMC
|
||
|
|
1f1fe647f2 |
port: end the boot when BOTH builds have arrived, not when the plate lands
Moving the plate onto the shared clock moved the boot's exit with it: the run quit at the overlay's settle (t=238) while build 4's own fade-in from black runs to t=261. pteff00 is still ~7 % opaque there, so the capture came out visibly darker than the previous one -- with nothing failing, no warning, and no line in the log to say why. Caught only because there was an earlier capture beside it. The boot now ends at max(view.settle_time(), overlay.settle_time()) and prints the unit it is waiting for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WM5XL4HfrHuxz8RiMWdCMC |
||
|
|
c225852fb3 |
port: the ring spins, the plate needs no constant, and rest.t was never the settle
Two milestones' known-wrong bits, both now answered by the RE agent, both taken. P5 -- the focus ring. It was drawn at 0 with a comment saying so. The period is now measured (continuous spin, eight evenly spaced autocorrelation peaks over nine revolutions, no angle estimated anywhere) and it needs NO authored constant: the period is the element's own declared t=120, and what the measurement adds is only that the turn repeats rather than stopping -- which "groups hold" could not decide, because 0 and 360 are the same pose. `spin_period_units` is structural and narrow on purpose: two keyframes, differing in nothing but rotation_deg, by a full 360, first timed and second untimed. 16 of 212 elements in this export match and all 16 are focus rings, zero false positives. That check is the point -- the measurement was taken on ONE button of ONE screen, and a rule that caught anything else would be extrapolating it to elements nobody watched. Verified on the port's own render with the RE agent's own control: bit-identical one period apart across the whole frame, 3.6/255 inside the ring's box at quarter-period steps, and box luminance conserved to 0.027 % over eight phases -- which is the observable they used to separate rotation from a pulse. Not claimed: direction (no signed angle was ever measured) and phase across a focus change (their run held focus throughout). P3 -- the plate. Last iteration I refuted their authoring instruction and shipped it anyway rather than pick between two of their numbers. The refutation held and the answer came back better than either option I offered: AUTHOR NOTHING. Both builds run on one clock started together and the plate arrives at its own declared t=238. The 2.13 s constant is deleted. The premise that failed was mine: rest.t IS NOT WHEN A SCREEN SETTLES. It is the last hold keyframe before the exit. ptlogo1 stops MOVING at t=42 and then creeps 5 px and 31 alpha steps to t=251. Reading rest.t put build 4's arrival at 4.350 s instead of 1.967 s, and the "2.51 s, which is not a landmark of anything" I sent them is that error wearing a decimal point. 238 - 118 = 120 units = 2.000 s against a measured 2.135 s at 28.1 fps presentation. Checked against my own export before touching anything. `ScreenView.settle_time()` still uses rest.t, and so the boot sequencer paces every screen off the wrong landmark. NOT changed here: "visible arrival" is a heuristic and getting it wrong re-paces everything. Filed, and asked for a timed boot instead now that their oracle is live. REFUTATION: two of their pages measure the same declared 120 units of wall clock during a static hold and disagree by 2 % -- plate 2.135 s (28.10 fps implied), ring 2.177 s (27.56 fps). That is seven times the plate page's own 6 ms run-to-run agreement, and it lands on the argument that page uses to justify itself: "the build-in is where frames are dropped; the static hold is not". Also the ring page's band, 27.6-28.8 fps, does not contain its own measurement -- the mean needs 27.56 and four of seven spacings are outside. Filed, not worked around: my port uses the declared 120 units either way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WM5XL4HfrHuxz8RiMWdCMC |
||
|
|
9b30fb5933 |
port: P3 -- the boot title gets its PRESS (A) plate, and two of the RE agent's numbers do not agree
ScreenView now draws two builds at once, which it never had to before. It is a second ScreenView in the same SubViewport rather than a subordinate screen inside one: each build has its own timeline, its own textures and its own hold, which is the entire content of the finding, and Node2D siblings already paint in tree order. `paint_order` still means what it meant -- an ordering WITHIN a build. The delay is authored in flow.json on the BOOT STEP, not on the `title` screen. What was measured is the boot title; whether the plate is there when the title is reached again -- (B) from the menu, or after the attract movie -- is not, and hanging it on the screen would quietly claim that it is. REFUTATION, and it is the substance of this commit: the RE agent's authoring instruction does not reproduce the RE agent's own measurement, and the gap is 3.97 s. The instruction is "when build 4 has settled, wait 2.13 s, composite build 2". But build 2 has a group and this port plays groups -- ptbtn00 is alpha 0x00 at t=214, still 0x00 at t=236 while it slides 10 px up, and 0xff only at t=238, which is 3.967 s at 60 units/s. So the plate is first VISIBLE at settle+6.10 s, while what was measured -- the glyph counter leaving 154 -- is visibility at settle+2.13 s. Both groups starting together puts it 0.38 s BEFORE settle; build 2 starting at settle puts it at settle+3.97 s; landing on the measurement needs build 2's group to start 2.51 s after build 4's, which is not a landmark of anything. The measurement is untouched -- it is an observation of the running game and I have no standing to doubt it. What is refuted is the step that turns it into an authoring rule. So the port ships the instruction, prints the discrepancy on every boot, and files the row. Same call as the BGM sub-waves: a port that quietly picks the number that looks right destroys the evidence, because a corrected boot looks exactly like a correct one. Also refuted, and it was mine: BLOCKED.md has said since P2 that "no element's alpha reverses direction anywhere in this export, so nothing pulses". ptbtn00 reverses -- 0x00 -> 0xff -> 0x00 -- and it was in the export the whole time. The claim had been checked against the screens P2 happened to be animating. The port still draws no pulse, because no reading of this group yields the measured 2.24 s: the whole group is 4.47 s and from its first keyframe 0.90 s. Gate: `--boot --capture=` writes one frame of the composited end state, instead of the 600-PNG filmstrip that was previously the only boot artifact. `--screen=title --overlay=press_start` raises the same composite in two seconds for anyone who does not want to sit through 137 s of Theora. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WM5XL4HfrHuxz8RiMWdCMC |
||
|
|
d6252481e1 |
port: P6 -- the menu has sound, and the BGM I "chose" was decoded all along
The three Static.slb cues and the menu bed now export to Ogg Vorbis and play.
`sylpheed_formats::media` does the assembly; nothing in port/ has heard of XMA.
Three things this milestone got wrong before it got right, all recorded in
docs/port/DECISIONS.md because the corrections are the useful part:
1. The cue offsets were a Rust `const` in the exporter. They are MEASURED, not
decoded -- a measured value compiled into the exporter is a measurement
wearing the costume of a decoded field, and nobody deletes it because nobody
can see it. They are authored/audio.json now.
2. I picked BGM_001 and wrote a careful `why` calling the choice arbitrary. The
menu's music is BGM_103, and it is in HANDOFF at
|
||
|
|
76979c799c |
port: P5 end to end -- and the port's title never says PRESS (A)
The P5 gate walk starts on a screen. This runs the whole objective instead, and
it is the only thing that would have found what it found:
xvfb-run -a godot --path port -- --boot --play \
--script=accept,down,down,down,down,accept,cancel,cancel --shots=/tmp/e2e
publisher wordmark -> developer logos -> ADV (151.9 s) -> title -> (A) -> main
menu -> navigate -> (A) -> EXTRAS -> (B) with focus restored to ptbtn05 -> (B)
-> title. 166.76 s, exit 0, nine frames. Shared as 1788003274-e68367e787d5.
THE PORT'S TITLE DOES NOT TELL THE PLAYER TO PRESS (A). The boot's last step is
`title` = GP_TITLE build 4, and build 4 has NO `PRESS (A) BUTTON` plate. P5 has
just made (A) the only way off that screen.
Not a guess about the art -- both states are captured off the running game and
differ by exactly that plate (live-title-build4-no-plate.png vs
live-title-press-a.png), and the plate is ALREADY EXPORTED as `press_start`,
build 2, sitting in export/screens/title/ unused by anything.
RECORDED, NOT FIXED, and the distinction is the point. This is P3's gate that
P5 exposed, and fixing it needs two things the port does not have:
* WHICH state an idle post-boot title shows -- build 4 alone, build 4 with the
plate over it, or build 4 THEN the plate after a delay -- is BEHAVIOURAL.
The game demonstrably has both states and nothing says which follows the
intro. The port has no oracle for a sequence; that is the Decoder's.
* showing it means DRAWING TWO BUILDS AT ONCE, which this port has never done
-- every mode loads exactly one screen. That is a change to ScreenView, not
a line in flow.json, and it is not being smuggled in under a navigation
milestone on the strength of "it looks more right".
Filed in BLOCKED.md. P5's gate is (A) into a submenu and (B) back; both work.
Two smaller things the same run found, both fixed:
* the boot step's `why` still said "nothing takes the title's place until P5
gives it somewhere to go". P5 has. Now says what is true: `--boot` STOPS on
the title (a boot that ends by fading to black looks like a crash) and
`--play` HANDS THE HELD TITLE OVER -- the stop is not a bug and the handover
is not another boot step.
* an empty focus printed as a line that trailed off, reading like a value had
gone missing rather than like there is none. The title is a screen with no
`buttons` that still takes (A), so it now prints
"(none -- this screen has no focusable item)".
Also confirmed: entering a submenu directly (`--menu=extras`) and pressing (B)
enters the parent at its AUTHORED initial focus, not a restored one. There is no
history to restore and MenuFlow.cancel only claims a restored focus when the
stack agrees about where it is going.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CtmUw5N5LJaMW1Njb8Ziey
|
||
|
|
1defbe0a32 |
port: P5 -- the menus navigate, and the focus ring is drawn wrong on purpose
P5's gate is "a human clicks through it". The artifact is a scripted walk that
proves the wiring rather than the intent -- up (wraps 01->05), five down, (A)
into EXTRAS, down, (B) back, landing on the main menu with focus RESTORED to
EXTRAS, ten PNGs one per settled step:
xvfb-run -a godot --path port -- --menu \
--script=up,down,down,down,down,down,accept,down,cancel --shots=/tmp/p5
--script posts InputEventAction through Input.parse_input_event so the presses
arrive at _unhandled_input exactly as a d-pad's would. Calling MenuFlow directly
would have been shorter and would have proved nothing: the wiring between a
press and the cursor is the part most likely to be broken, and a direct call is
exactly the part that skips it.
Derived vs authored, which P5 is the easiest place to blur:
* DERIVED -- the ORDER of the items, from each screen file's `buttons`, which
the exporter already fills from button-role elements sorted by resting Y.
* AUTHORED -- destinations, initial focus, what (B) does, and left/right being
a no-op. All measured off the running game (HANDOFF Q4/Q5) or chosen, none
on the disc, all in authored/flow.json with a why.
Four of five main-menu destinations are `goto: null` with a `blocked` note. That
is a MILESTONE BOUNDARY, not an unknown -- DIFFICULTY, the save list, the lesson
list and OPTIONS were all measured and live in archives this export does not
carry. `blocked` and `none` are kept apart so nobody later "discovers" the gap.
--headless CANNOT DRAW, and the port hung instead of saying so.
Measured, not assumed: under --headless Godot's dummy renderer never emits
RenderingServer.frame_post_draw, so every capture path awaited it forever --
--capture since P1, --film since P3, --shots as of now. With stdout block-
buffered the observable behaviour was SILENCE, FOREVER, which in a loop reads as
a job still working. Isolated by `--quit` (prints, exits 0) vs `--capture` (zero
bytes, killed at 40 s). Now those three flags refuse at STARTUP naming the
xvfb-run line that works, and --script no longer waits for a frame it is not
going to photograph -- so headless walks the menus in 4.5 s as a cheap
regression check needing no X server.
REFUTATION ATTEMPT, against the Decoder's
|
||
|
|
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.
|