Commit Graph

4 Commits

Author SHA1 Message Date
Sylpheed port agent
6e46b6a136 docs: the P1 diff, and the one question it raised
`main_menu` -- the milestone's gate -- agrees with the reference renderer to
within 3/255 on every channel of every pixel, RMSE 0.38 %, no pixel above 4 %.
Nine of the twelve screens are at or under that; two are byte-identical.

Three exceed it, and each gets a cause rather than a wider tolerance:

* `title` (max 6): every disagreement is INSIDE A TIE -- the derived order and
  the CLI's measured-off-the-game order differ only among elements with
  identical layer keys. That is exactly the residual HANDOFF Q3 documents and
  the export already declares in `unresolved: paint_order_ties`. Cost: 904 px at
  4-6/255 in one glow band. The port keeps the stable sort; fitting it to one
  screen's capture would be tuning.

* `title_jp` (max 154): `ptlogo_eff2` at 125 % is the only drawn element in the
  whole export at a scale that is not a whole multiple of 100 %, and `title_jp`
  is the only screen over 6/255. Same fact twice. `ui_layout::blit` samples the
  source at the destination pixel's top-left corner; a GPU samples at its
  centre, and at 125 % those disagree on one column in five.

  I think the CLI is the one that is wrong -- corner-sampled nearest is a
  half-pixel bias toward the top-left that no rasteriser produces. But that is a
  reading, not a measurement: it needs a framebuffer capture of the Japanese
  title screen, so it is filed in BLOCKED.md as a question. The port is NOT
  changing to match, because matching would mean reproducing a half-pixel offset
  on purpose to make a number smaller.

* `extras` (max 4): two pixels.

Also reconciled: the pivot question predicted a P1 diff could not distinguish
the declared pivot from half the texture, because both renderers use the
declared one. That held. Recorded so the agreement is not later mistaken for
evidence -- and P2 will not settle it either.

BLOCKED.md's `/reborn` HEAD updated to 690683d, with a note that the mount is
read-only by design and `git -C /reborn pull` fails rather than being skipped.
2026-08-28 19:31:21 +00:00
Sylpheed port agent
9847adf383 docs: FORMAT v2, what P0 decided, and BLOCKED reconciled
FORMAT is bumped to v2 with a "Changes from v1" table giving a reason per row.
v1 was written before HANDOFF answered Q1 and Q3 and before the two-colour
modulate was known; each change is a thing v1 could not have said. The paint
order moves from `unresolved` into the export, because Q3 decoded it — a u16
layer key at +0x0A, stable-sorted — and a decoded answer is read in the
exporter. `paint_order_ties` replaces it, because the tie-break is still
unknown.

Where a layer key is not in the file it comes from the decoders' table of keys
measured off the running game. That is a different kind of fact, so it is
labelled: `layer_source` is "sprite", "implied" or "none".

BLOCKED is reconciled against HANDOFF at /reborn d69272e. Seven of its ten rows
are answered and are moved out; what remains is Q8 (no cue-to-event binding),
the two Q10 unknowns (which BGM, and where a loop restarts), Q9's unsettled
skippability, Q4's untested NEW GAME, and Q6's undecoded boot driver.

It also raises one question back, found by counting the export rather than by
reverse engineering anything: the decoders document a .t32 element's pivot as
"exactly half the decoded texture's dimensions (verified 7/7 on the tutorial
bundle)", and on GP_TITLE that holds for 55 of 93 sprite-bearing .t32 elements.
38 do not, some grossly — ptlogo_back2 is 1118x262 with pivot (500,117) where
half is (559,131). It changes nothing today, because the exporter emits the
declared pivot and the pivot only matters when scale != 100%. But scale IS
animated here — 177 keyframes across GP_TITLE are not 100%, including on the
title screen P1 has to draw — so the question of what the running game anchors a
scale to is worth an answer before P2. Noted there that the port and
`sylpheed-cli screen render` make the same choice, so a P1 diff cannot
distinguish them and their agreement is not evidence.
2026-08-28 18:43:52 +00:00
MechaCat02
0b18cd0484 port: pin Godot 4.7.2, and decide the MCP/skills question
The pin was 4.3, chosen without checking; 4.7.2 is the current stable (released
2026-08-18). Getting this right matters more here than usual because the project
diffs Godot screenshots against a reference renderer, so the engine version is
part of the measurement.

No Godot MCP server. Not because they are bad -- the mature ones need a LIVE
editor plus a WebSocket plugin and a Python server, which is a daemon, an editor
process and a second runtime added to an unattended loop. And their headline
feature, scene-tree introspection and node manipulation, is built for someone
hand-authoring scenes in the editor. This port GENERATES screens from exported
JSON: the agent writes a loader, not a scene tree, so the feature that justifies
the complexity does not apply. What it actually needs to verify itself -- run
headless, screenshot, diff against sylpheed-cli screen render -- is already a
bash job.

Third-party skill packs are the opposite trade: pure context, no runtime. Worth
revisiting, deliberately not installed now, because a skill is instructions
injected into an agent running with approvals disabled -- a supply-chain
decision, not a default -- and because P0/P1 need no advanced GDScript. The
agent may propose one, naming the milestone it unblocks, for a human to vendor
and review.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 20:07:39 +02:00
MechaCat02
38541c153b scaffold the Godot port, as its own repo with its own agent
The port is deliberately separate from the reverse-engineering project: its own
repository, its own clone, its own container. Two writers in one working tree
means files change under whoever is mid-edit and a `git add -A` by one sweeps up
the other's work -- which happened today in the Reborn tree, so this is set up
not to repeat it.

The wall: Godot never reads a disc format. An offline Rust exporter converts the
user's disc into JSON + PNG + Ogg, and the Godot project reads only that. No
GDExtension, no Rust in port/. Beyond the practical reason -- Godot cannot read
IPFB, RATC, T8aD, XMA or WMV -- there is the design one: modding is a goal, and
if the runtime reads the original formats then modding means reverse
engineering, whereas if it reads JSON it means opening a file.

The decoders come from sylpheed-formats PINNED BY REVISION (47f423f), not
vendored and not reimplemented. `sylpheed_formats::media` in particular already
owns every case where one playable thing is not one archive entry: entries that
span segment files, banks with several sub-waves, and the cutscene voices, which
are one continuous XMA stream chunked into VOICE_*.slb entries whose boundaries
do NOT match the cues. That last one is the easiest thing in this project to get
subtly wrong, so the mission says outright not to re-derive it.

docs/MISSION.md is the objective (P0-P7, each gated by an artifact rather than
by compiling). docs/BLOCKED.md lists what cannot proceed until the RE agent
answers Q1-Q10, and says plainly that none of it may be guessed -- this agent
has no emulator and no oracle, so a value it invents is indistinguishable from a
decoded one a month later.

The container is deliberately small: 3 cpus / 4 GB against the RE container's
6 / 7, and an image with no C++ toolchain, no Vulkan stack and no emulator. Two
full-size containers do not fit on this box beside a desktop.

Its launcher sets the git identity through GIT_AUTHOR_*/GIT_COMMITTER_* rather
than writing [user] into .git/config -- the config route captures every commit
made in that tree, including a human's, which is how six of today's commits
ended up attributed to the RE agent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:56:44 +02:00