The Decoder taught `ui_layout::blit` the additive blend at formats-pin-2026-09-01b, on the argument this port made: the comparison was structurally incapable, and the refutation keeping additive out was ⟨render-vs-capture⟩ -- that renderer disagreeing with itself while it had a stale keyframe association, no leaf geometry and no rotation. MEASURED WITHOUT PULLING THEIR BRANCH INTO MINE: a detached worktree at the tag, sylpheed-cli built there, verify-screen pointed at it through SYLPHEED_CLI. My branch is untouched and the reference on `main` is unchanged. Pre-registered: the diffs caused by the gap should collapse, and whatever remains is a different cause. main_menu 7.2580 max 105 -> 1.2068 max 28 6.0x main_menu_jp 7.3440 max 108 -> 1.2111 max 31 6.1x extras 6.9757 max 113 -> 1.0229 max 28 6.8x extras_jp 7.0734 max 115 -> 1.0255 max 30 6.9x title 1.0335 max 88 -> 0.5685 max 41 1.8x title_jp 4.4944 max 233 -> 2.8225 max 233 1.6x build_12/15 0.0772 max 60 -> 0.0463 max 17 1.7x build_00/01 0.0676 max 60 -> 0.0366 max 4 DIFFERS -> OK build_00 and build_01 stop differing entirely, over3 3422 -> 0. And the twins agree to a third of a percent: main_menu 1.2068 vs main_menu_jp 1.2111 (0.36 %), extras 1.0229 vs extras_jp 1.0255 (0.25 %). Under the name-keyed map those pairs were 4.1x and 7.8x apart. Nothing was fitted -- they converged once when the PORT took the decoded field and again now that the REFERENCE draws the same blend. The residual is smaller and its causes are the documented ones: title_jp 2.82 is the --pose=rest sparkle handling, title 0.57 the ptloop sweep phase, both untouched by any of this. main_menu/extras and twins sit at ~1.0-1.2 max ~28-31, new and undiagnosed -- additive was the dominant cause, not the only one. And build_12/15 do NOT return to their pre-change 0.0368, so pgloading_loop5 carries a small residual of its own beyond the blend. Recorded, not chased. 🔴 THE ALLOWANCE SHOULD NARROW AND HAS NOT, ON PURPOSE. Its justification is gone, but check-all builds the reference from the WORKSPACE crate and the additive path is at a tag, not on `main`. Narrowing now would turn check-all red against a reference that still cannot draw additive -- a wall of failures meaning one thing, which is the defect the display guard exists to prevent. The trigger is written into the script so it does not rot: when `grep -q additive crates/sylpheed-formats/src/ui_layout.rs` succeeds, drop the export-derived clause and keep the two named legacy rows. The set that should then differ is measured above. H5 confirmed from the other side. And a trap worth carrying: the Decoder nearly reported my H5 claim as a contradiction because `loop5` is not a sprite -- it is an ELEMENT, pgloading_loop5.rat, resolving to sprite pgloading_ring.t32. Any census keyed by sprite name will miss it. Not settled: the ~1.0-1.2 residual on the menus; pgloading_loop5's own residual; finding 3, where the Decoder is taking the clock origin; and the 8 claims R1 has re-opened against ⟨render-vs-capture⟩, including BOTH legs of the rest() pair that this port's settle behaviour rests on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX
Sylpheed
A clean-room reverse engineering and port project for Project Sylpheed: Arc of Deception (Xbox 360, 2007).
Three things live here, in one repository so that a change spanning them lands as one commit:
| The decoders | crates/sylpheed-formats — the disc's formats, read and verified disc-wide |
| The port | port/ — a Godot 4 project, plus crates/sylpheed-export which converts a disc into the open asset tree it reads |
| The corpus | docs/re/ — what has been reverse engineered, with its evidence, its retractions and its dead ends |
You need your own copy of the game. No game content is in this repository and none ever will be. The exporter reads the disc you supply.
The oracle is the real game
sylpheed-cliand the Explorer are tools for verifying our decoding. They are hypotheses under test and they have been wrong. When something must be checked against the truth, the truth is the game running in Xenia Canary, captured — not any renderer of ours.
This is stated first because getting it backwards is the most expensive mistake this project has made.
Layout
crates/
sylpheed-formats/ the decoders. Disc-wide verified; the corpus is its spec
sylpheed-cli/ headless tools -- render a screen, dump a table, probe audio
sylpheed-viewer/ the Explorer: a human's window onto the disc. STATIC data only
sylpheed-export/ disc -> the open, moddable asset tree
port/ the Godot 4 project. Reads open formats ONLY
authored/ decisions that are NOT on the disc, each with its reason
data/
base/ generated by the exporter. Gitignored, never hand-edited
mods/ drop-in overrides. Yours
docs/
re/ the corpus: findings, refutations, method traps
game/ how the game is navigated -- menus, modals, flight
port/ the port's mission, its handoff contract, modding rules
-- and RUNNING.md, which is how you actually start it
agents/ how the agent team works together
tools/ capture harnesses, probes, the share tool
exchange/ transient inter-agent files. NOT in git
docker/ the agent containers
Where to start
docs/re/INDEX.md— what is decodeddocs/re/REFUTED.md— what has been tested and dieddocs/re/METHOD.md— traps this project has already paid fordocs/game/navigation.md— how the game is navigateddocs/port/MODDING.md— why the asset tree looks the way it does
Xenia Canary is a separate repository: it is a fork tracking upstream, and it carries our instrumentation.
Conventions
Confidence is per claim, never per document: ✅ CONFIRMED · 🟡 PROBABLE ·
❔ HYPOTHESIS · ❌ REFUTED. A withdrawn result is kept with its reasoning
rather than deleted — that is why the numbers here can be trusted.