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 (8b6dbcf), 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>
86 lines
4.2 KiB
Markdown
86 lines
4.2 KiB
Markdown
Build the Godot menu port, one milestone at a time.
|
|
|
|
## Your objective
|
|
|
|
`docs/MISSION.md` — read it every iteration. It defines the milestones P0…P7 and
|
|
the gate each must pass, the wall between the exporter and Godot, and the
|
|
derived/authored split.
|
|
|
|
**You do not reverse engineer.** A separate container agent does that, in the
|
|
Syplheed-Reborn repository, mounted read-only at `/reborn`. You have no emulator
|
|
and no oracle, so a guess of yours is indistinguishable from a fact and will be
|
|
believed later. If you need an answer the disc has not given you, write it in
|
|
`docs/BLOCKED.md` and move to another milestone.
|
|
|
|
## Read these first, every iteration
|
|
|
|
1. `docs/MISSION.md` — milestones, gates, scope.
|
|
2. `/reborn/docs/port/HANDOFF.md` — **the contract.** What is decoded, what was
|
|
measured off the running game, and what is known undecodable. `git -C /reborn
|
|
pull` first; the RE agent publishes continuously.
|
|
3. `docs/FORMAT.md` — the open format. It is versioned and it is yours to
|
|
revise, but a change is a deliberate act with a version bump.
|
|
4. `docs/BLOCKED.md` — what you are waiting on, so you do not re-discover it.
|
|
|
|
`/reborn/docs/re/disc-atlas.html` maps how the assets reference each other.
|
|
|
|
## Each iteration
|
|
|
|
1. **Pick the lowest unfinished milestone.** If it is blocked on an RE answer,
|
|
record that in `docs/BLOCKED.md` and take the next one that is not.
|
|
2. **Build the smallest thing that reaches its gate.** The gate is an artifact —
|
|
a validating JSON file, a screenshot, a clickable build — never "it compiles".
|
|
3. **Keep derived and authored apart.** `export/` is regenerated wholesale and
|
|
never hand-edited. A fix you are tempted to make there belongs in the exporter
|
|
or in `authored/`, and every `authored/` entry carries a `why`.
|
|
4. **Write down what you decided**, in `docs/`. A decision that lives only in
|
|
your context is lost when the container dies.
|
|
5. **Commit** to `auto/<topic>`, one logical change per commit.
|
|
6. **Publish**: `push-work`. Every iteration that produced a commit.
|
|
7. **Say plainly what you did not settle**, and stop.
|
|
|
|
## Hard rules
|
|
|
|
* **Never commit game assets.** `export/` is gitignored and generated from the
|
|
user's own disc. Code, schemas, `authored/` mappings and docs only.
|
|
* **No Rust in `port/`, no GDExtension.** If Godot cannot read something, the
|
|
exporter emits it differently.
|
|
* **Do not vendor or reimplement `sylpheed-formats`** — it is pinned by revision.
|
|
In particular do not reimplement media assembly: `sylpheed_formats::media`
|
|
already handles segment-spanning entries, multi-sub-wave banks and the
|
|
continuous cutscene-voice stream, and that last one is the easiest thing here
|
|
to get subtly wrong.
|
|
* **`/reborn` is READ-ONLY.** Never commit there, never edit it. It belongs to
|
|
the other agent and you share no working tree with it.
|
|
* **Never commit to `main`**, never rebase a shared branch, never rewrite history.
|
|
* **Do not adopt a runtime dependency on your own authority.** Propose it.
|
|
|
|
## Verifying
|
|
|
|
* `sylpheed-cli screen render` (built from `/reborn`) is the reference renderer.
|
|
When Godot draws a screen, diff against the CLI's composite of the same build.
|
|
Where they disagree, one of them is wrong — say which, and why, rather than
|
|
tuning until they match.
|
|
* Godot runs headless (`godot-headless`), and windowed under Xvfb with
|
|
`screenshot` for a capture.
|
|
* A regenerated `export/` that comes out byte-identical is strong evidence a
|
|
change was additive. When it does change, check that every diff line pairs.
|
|
|
|
## Publishing
|
|
|
|
`push-work` pushes the current branch to origin. It refuses anything that is not
|
|
`auto/*` and never force-pushes, so the consolidated line stays a human's
|
|
decision. Run it **every iteration that produced a commit** — not at the end of
|
|
some longer arc, which is exactly when a container dies.
|
|
|
|
If it reports no credentials, say so in your reply and continue working. Do not
|
|
improvise another route out.
|
|
|
|
## Pacing
|
|
|
|
One milestone step plus its write-up is a good iteration; a marathon is not. Stop
|
|
with a clean commit, a push, and an honest list of what is still open.
|
|
|
|
The loop runs on a fixed interval set by the harness, so you do **not** need to
|
|
arm the next wakeup yourself. Spend that attention on the write-up instead.
|