# Primary objective — the menu shell, running in Godot **Status:** active, set 2026-08-28. Build a Godot 4 project that boots the player's own disc through the sequence the real game uses, and let a person move through it: ``` developer logo splash → intro video → title / PRESS Ⓐ → main menu → submenus ``` No gameplay. No 3D. No HUD. No emulator. Done means a human presses a d-pad and Ⓐ and moves through those screens with the right art, animation, music and transitions. ## 1. You are one of two agents A **container agent** does the reverse engineering, in the [Syplheed-Reborn][reborn] repository. It runs the emulator; you do not. You build the port from what it publishes. The contract is `docs/port/HANDOFF.md` in that repository. **Read it before assuming any value is on the disc.** Every answer there is one of three things, and the distinction decides what you do: | | meaning | what you do | |---|---|---| | **decoded** | a field on the disc, with a disc-wide check | read it in the exporter | | **measured** | not on the disc, but the running game does *this* | put it in `authored/`, cite the finding | | **undecodable** | looked for, provably not there | put it in `authored/`, say it is a decision | If HANDOFF.md does not answer something you need, **say so and move to another milestone**. Do not guess and do not reverse engineer it yourself — you have no emulator and no oracle, so a guess here is indistinguishable from a fact and will be believed later. [reborn]: https://git.mc02.dev/fabi/Syplheed-Reborn ## 2. The wall The Godot project **never reads a disc format**. No IPFB, no RATC, no T8aD, no XMA, no WMV. If Godot cannot read something, the exporter's job is to emit it differently — not to bridge the gap at runtime. * **No GDExtension. No Rust in `port/`.** * The decoders come from `sylpheed-formats`, **pinned by revision**. Do not vendor them, do not reimplement them, and do not float the pin — a decoder change landing mid-milestone is exactly the confusion this pin prevents. * Bump the pin deliberately, as its own commit, saying what you wanted from it. **In particular, do not reimplement media assembly.** `sylpheed_formats::media` already handles the cases where one playable thing is not one archive entry: a `.pak` entry that spans segment files, a bank 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, so *a `.slb` need not hold the track its name claims*. That last one is the single easiest thing in this project to get subtly wrong. Use `resolve_movie_voice_region`. ## 3. Derived vs authored | | `export/` | `authored/` | |---|---|---| | produced by | the exporter | you, by hand | | contains | what the disc says | what we decided | | hand-edited | **never** | always | | in git | **no** — gitignored | yes | | on re-export | overwritten wholesale | untouched | Tempted to hand-fix a file under `export/`? The fix belongs in the exporter or in `authored/`. Every `authored/` entry carries a `why`. When the RE agent later decodes something you had authored, **delete the authored entry** and let the exporter emit it. That deletion is the measure of progress. ## 4. Never commit game assets `export/` is generated from the user's own disc and is gitignored. Code, schemas, `authored/` mappings and docs only. If you are about to commit a sprite PNG or a transcoded video, stop. ## 5. Milestones A milestone is done when its **artifact** exists, not when the code compiles. | | Milestone | Gate | |---|---|---| | **P0** | Exporter skeleton; one screen and its sprites to `export/` | `export/screens/title/main_menu.json` validates against FORMAT.md and the PNGs open | | **P1** | Godot renders that screen statically at 1280×720 | A Godot screenshot beside `sylpheed-cli screen render` of the same build — they should agree, and where they do not, say which is wrong | | **P2** | Keyframe animation | Buttons slide in. **Blocked on HANDOFF Q1** (the time unit). Do not invent it | | **P3** | Splash → title, with the transition | Both screens back to back, unattended | | **P4** | Intro video | `ADV.wmv` plays with audio (§6) | | **P5** | Main menu: navigation, focus states, Ⓐ into a submenu, B back | A human clicks through it | | **P6** | Audio — menu BGM and move/confirm SFX | Sound on the P5 gate. **Looping is blocked on HANDOFF Q10** | | **P7** | New-game intro video after NEW GAME | Plays, then returns to a defined state | Work the lowest unfinished milestone. When one is blocked on an RE answer, say so in `docs/BLOCKED.md`, and take the next milestone that is not. ## 6. The video problem `ADV.wmv` is **WMV3 video with WMA Pro audio**, 1280×720 at 30 fps, 137 s. Godot 4 plays only **Ogg Theora** natively. Transcode with ffmpeg, and **record the exact command in the export manifest** so a modder who dislikes the quality can re-run it rather than reverse-engineer what you did. Theora at 720p is not great; if the result is visibly poor, **say so and propose** the FFmpeg-GDExtension fallback — do not adopt a runtime dependency on your own authority. Only the boot intro and the one new-game intro are in scope. The disc holds 3.3 GB of video; transcoding all of it is not this milestone. ## 7. Out of scope 3D, gameplay, HUD, missions, save/load, localisation beyond English, the Ready Room, and any reverse engineering. If you want an answer the disc has not given you, that is a request to the container agent, not a task for you. ## 8. Tooling policy — MCP servers and third-party skills Surveyed 2026-08-28. **No Godot MCP server, for now**, and the reason is not that they are bad: * The mature ones ([godot-ai][ga], and most of the field) need a **live Godot editor** running with a plugin that talks WebSocket to a Python server. This agent is headless in a container; that is a daemon, an editor process and a second language runtime added to an unattended loop, all of which can fail in ways that look like a port bug. * Their headline feature is **scene-tree introspection and node manipulation** — built for someone hand-authoring scenes in the editor. This port *generates* its screens from exported JSON at runtime. The agent writes a loader, not a scene tree, so the feature that justifies the complexity does not apply here. * What the agent actually needs to verify its work already exists: `godot-headless` to run the project and `screenshot` to diff against `sylpheed-cli screen render`. The verification loop is the valuable part, and it is a bash job. **Third-party skill packs** ([godot-claude-skills][gcs], [GodotPrompter][gp], [Godot-Claude-Skills][rcs]) are the opposite trade: pure context, no runtime, no daemon. They are worth revisiting. They are **not installed now** because a skill is *instructions injected into an agent running with approvals disabled*, which is a supply-chain decision and not one to make by default — and because P0/P1 are a Rust exporter and a static sprite draw, which need no advanced GDScript. **If you want one, propose it**: name the pack, say which milestone it unblocks, and let a human vendor and review it. Do not install from a marketplace on your own authority. Revisit this if GDScript quality becomes the bottleneck — most likely at P2, where keyframe ramps meet tweens. [ga]: https://github.com/hi-godot/godot-ai [gcs]: https://github.com/alexmeckes/godot-claude-skills [gp]: https://github.com/jame581/GodotPrompter [rcs]: https://github.com/Randroids-Dojo/Godot-Claude-Skills