eb3f75d0f5fd7ccbdca7a679ce07e6e695ff6b72
20 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c909d1dc49 |
port: the contract I read is 3185 lines shorter than the contract
docs/port/HANDOFF.md on main is 926 lines, last touched |
||
|
|
ad8e18de7c |
port: sweep instructions above descriptions -- the silent class is clean, two loud hits
Their sharpening: a stale instruction manufactures a false confirmation, strictly worse than a stale description that merely misleads. Applied to my instruction surface, the documented invocations in tool and script headers. All fifteen distinct flags across those examples are parsed, so nothing in my headers can produce their failure mode by being inert. But 'parsed' is a proxy and its gap is known -- --shots parses and does nothing on the --boot path -- so I ran two documented examples end to end rather than trusting the grep, and both produce a 1280x720 frame. Two hits, both loud rather than silent: 11 references to tools/verify-capture and tools/verify-screen, paths that do not exist since the tools are under tools/port/ (fixed in 4 files); and check-all claiming eleven tools where there are fourteen (now states both so the sentence dates itself). The distinction worth recording: mine fail loudly, theirs failed silently. A wrong path announces itself; an inert environment variable returns a clean wrong result. Both are stale instructions and only one manufactures evidence. Honest limit: I tested the flag surface plus two examples end to end, not all thirteen documented invocations -- the --boot ones take 156 s each. That is a judgement about cost, not a claim of coverage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
8fac01ed4e |
port: sweep for their tell -- two descriptions my own code had already refuted
Their sweep cleared my three quoted figures: the splash dwell spans were corrected today, and the gap and cold-boot intervals are capture-derived, which the record-layout fix cannot reach. Their sharpening -- the tell is not that a number changed but that a page hedges a quantity it should not need to -- made a sweep of my side possible. The marker is 'untimed', since there are 0 untimed keyframes in 866. Two hits, both worse than hedges: descriptions their own code had already refuted. Hit A: spin_period_units' doc stated the rule as 'the first timed and the second untimed', the pre-fix version, while the body directly below carries a block explaining that this rule silently stopped the ring and implements the span-based replacement. A function and its documentation disagreeing, with the documentation stating the refuted version. Hit B: exit_ramp_units' doc claimed the value is authored in timing.json and that the disc has no time slot on that keyframe. Both false -- the entry was deleted as progress and the corrected layout times every pose. And I wrote the correction myself, immediately below it, two iterations ago without looking up. Both are fixed code under an unfixed description, with the fix and the falsehood within twenty lines of each other -- not drift, but editing at the point of failure without re-reading the frame around it. Their practice of quoting the original sentence is adopted so the change is visible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
3751f86a86 |
port: the oracle harness was nondeterministic and I quoted it for a dozen iterations
verify-capture's main_menu row reads 13.30 / 13.27 / 13.25 / 13.26 across runs this session while every other row is identical to the digit. I cited those numbers repeatedly, including in the rest() adjudication. Cause: the focus ring spins on time_units raw rather than the pose clamped by holding -- deliberate and correct, since the ring is the one thing on a settled screen that keeps moving -- so its angle at capture is set by the wall clock. extras is stable because nothing there spins. --loop-phase already existed and did not cover it: it pins the looping focus record phase, while the spin is a second free-running clock I guarded once and never connected. Extended loop_phase_units to pin the spin too, and verify-capture now passes --loop-phase=0 at all four render sites. The control matters because the drift was intermittent -- three unpinned runs gave 13.25, 13.26, 13.26, so three pinned runs agreeing would prove nothing. Phases 0/30/60/90 give 13.2583 / 13.1991 / 13.2637 / 13.2588: the pin is live and the 0.065 spread is the whole of the observed drift. Non-finding recorded so nobody mines it: phase 30 scoring lowest is not evidence about the ring's real phase -- 0.065 against a ~13.2 gamma floor is 200x too small. A margin only means something against the noise it sits on. No conclusion changes: the smallest margin any of them turned on was 0.14% differing area. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
bd1415b29d |
port: the refuted 24-unit constant was living in a default; make it loud instead
ScreenView.exit_ramp_units defaulted to 24.0 -- the constant HANDOFF ask 2 told this port to author and that it refused, since the file's own ramp is 10 units. The authored entry was deleted as progress when the corrected record layout removed the unknown, and the default plus boot.gd's timing.get(..., 24.0) made that deletion a no-op. Both use sites are unreachable on today's export (866 keyframes, 0 untimed), so the branch is kept for an older export but no longer invents: the default is -1.0 meaning not supplied, and an untimed group now raises an error naming the screen rather than fabricating a duration. My first verification accused the change: main_menu 641941 px and extras 226009 px changed, on a branch that cannot execute and with no error raised. The cause was --screen=X --capture= firing at an uncontrolled instant -- t=9.00 in the earlier run against t=8.00 in the later one, one unit apart mid-build-in. Three runs now are byte-identical, so it is not noise; the instant is stable within a session and moves between them. Re-run with --time=1.0 pinned, old against new is byte-identical on all four screens. Records the harness limitation: --screen=X --capture= cannot be used for before/after comparison on an unsettled screen, which also explains the earlier settle-vs-rest confound. Also corrects my overstatement that other tools call the CLI -- verify-screen is the only one, checked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
9233ff1e08 |
port: the rest() flash defect reaches all four splashes I ship; the port survives it
The Decoder censused rest()'s dwell fallback from the file side: 2305 elements have no plateau, 1697 of those get a visible pose, and in GP_TITLE all four visible fires are on the splash screens this port ships. Confirmed in my export. Refinement to their description: they named the [0:a0 15:a255 30:a212 45:a0] shape, but palogo_gamearts_eff and palogo_seta_eff hold 255 through t=30, so their fallback lands on the flash PEAK rather than its decay. Same defect, worse pose. The port ships the right frame and there is now a number for it. Publisher splash against the committed oracle capture: timeline (shipped) RMSE 2.17 / 0.01% differing; --pose=rest 9.05 / 0.75% -- 75x the differing area on a screen I ship. So the rule added to verify-screen's header after the title_jp mistake generalises, and is demonstrated against an oracle rather than argued. What did need fixing: ScreenView logged '(transparent at rest)' for every skipped element whatever instant it posed, so it said that about palogo_sqex_eff, whose resting alpha is 212. That is the same rest-versus-posed-instant confusion that cost a wrong conclusion, pre-printed in the log. It now names the instant. Controlled both ways: timeline says 'at t=6' and skips the flash, --pose=rest still says 'at rest' and draws it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
81352459e3 |
port: verify-screen was nondeterministic; pin the pulse phase in the harness
Running the full set after the plate fix, press_start returned over3 5021, 8919, 5021 on three identical runs. The plate's looping focus record takes its phase from time_units, which free-runs, so the captured frame lands wherever the grab fell -- while the reference renderer cannot pulse at all. The port is not the thing that is wrong: the pulse is measured and a thing that pulses does not stop because the screen arrived. ScreenView.loop_phase_units pins it, negative means free-running and stays the default everywhere, and only the harness passes --loop-phase=0. Controlled: pinned, 3 runs identical; free-running, 3 of 4 identical and one different. That 3-of-4 is why it survived -- it looks deterministic most of the time, and without the negative control a no-op flag would have been indistinguishable from a fix. With the phase pinned press_start reads max 1 / over3 0 OK -- the recorded baseline exactly. Fifteen of sixteen rows now match. The sixteenth, title_jp, has genuinely drifted: 155/20498 -> 233/61208, deterministic, on the Godot side, localized to one 350x396 block at (405,74). There is no capture of the Japanese title, so I can say the renderers moved apart but not which moved. Recorded as an ask, not resolved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
aa6be8e809 |
port: the plate pulses -- restore the looping record, over the base rather than instead of it
Ask #1 measured: held at the title with no input the plate oscillates ~23 cycles per window with no decay and NEVER goes off -- floor 159 thresholded pixels, pulse bottoming at 714. A glow alone cannot do that (0 -> 80 -> 0); a steady base plus a pulsing glow can, and 714/1520 are base-only and base-plus-glow. The port already had the base right: ptbtn00's fade to 0 at t=244 is its EXIT ramp, and while held the base sits at alpha 255. The glow was missing. The defect was in the renderer, not the entry I deleted on 2026-08-29. _draw treated a looping record like a focused one -- drawn INSTEAD of the base, which is right for a focused button and wrong here. I deleted the right thing for the wrong reason. A looping record now draws OVER the base and the entry is restored. Rendered: 95.68 base only, 115.52 at peak, period 1.980 s against a declared 120 units = 2.000 s, ratio 0.990. Visible on the boot path; not under --screen --overlay, where a static overlay poses at its arrival by design. Authored in UNITS on their instruction, with their three limits verbatim: one boot, does not distinguish boot from attract title, and 714/1520 is not an alpha ratio so no duty cycle is inferred. 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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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.
|