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>
156 lines
7.5 KiB
Markdown
156 lines
7.5 KiB
Markdown
# 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
|