Coherence on BGM_103, the menu's bank, with controls run first: a real linear filter of wave 0 reads 0.93-0.94 in every band, a different bank reads 0.001, and wave 0 misaligned by 1 s reads 0.004-0.057. The measurement reads 0.027 at 1-4 kHz, so the 'wave 1 is wave 0 filtered' model is refuted. The frequency structure is inverted relative to any mic-pair or reverb model: coherence rises with frequency (0.169 -> 0.827) while energy falls (71 % -> 0.2 %), and a rear pair decorrelates fastest at HF. In the midrange the two waves are 13x further apart than the two channels of one wave. But the L-R control is what limits the tool and it is recorded as such: within one wave, genuinely one performance in two channels, coherence is only 0.221-0.497. So 'same performance' does not imply high coherence here, my positive control was the wrong model of the rear-pair reading, and the 🟡 is NOT settled. The tool tests for linear filtering and neither surviving reading requires it. Also corrects MISSION's Q10 row, which still carried the refuted three-sub-wave premise and had directed work at a dead question for days. Its gate is in fact met. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
406 lines
23 KiB
Markdown
406 lines
23 KiB
Markdown
# Primary objective — answer everything the menu port needs
|
||
|
||
**Status:** active, set 2026-08-28. This replaces "work the RE backlog" as the
|
||
agent's primary objective. It does not change what the agent *does* — it is still
|
||
reverse engineering — it changes what earns attention: an item is worth doing
|
||
when the menu port is blocked on it.
|
||
|
||
## Who builds what
|
||
|
||
**You do not build the port.** A separate agent will build it, from what you
|
||
produce. Your deliverable is decoded, verified, written-down answers plus the
|
||
reference data that proves them.
|
||
|
||
| | container agent (you) | port agent |
|
||
|---|---|---|
|
||
| decodes the disc | ✅ | ❌ — consumes your answers |
|
||
| measures the running game | ✅ | ❌ — no emulator |
|
||
| writes `docs/re/` and `docs/port/HANDOFF.md` | ✅ | reads them |
|
||
| Godot project, asset pipeline, transcoding | ❌ | ✅ |
|
||
|
||
If you find yourself designing an export schema or writing GDScript, you have
|
||
crossed the line. Stop and go back to the question you were answering.
|
||
|
||
`crates/sylpheed-viewer` is also **not yours to change** for this objective. The
|
||
Explorer is the human's tool for exploring and verifying the RE work, it keeps its
|
||
static-data-only rule, and the port does not depend on it.
|
||
|
||
## The target
|
||
|
||
Someone else has to build this, from your answers alone:
|
||
|
||
```
|
||
developer logo splash → intro video → title / PRESS Ⓐ → main menu → submenus
|
||
```
|
||
|
||
No gameplay, no 3D, no HUD, no missions. If an answer is not needed to put those
|
||
five screens on a display and let a person move through them with a d-pad and Ⓐ,
|
||
it is not in this objective.
|
||
|
||
## What is already answered
|
||
|
||
Do not re-derive these. They are in `docs/re/` and the
|
||
[disc atlas](../re/disc-atlas.html):
|
||
|
||
* **The screen archive.** `GP_TITLE.pak` holds the whole title-side tree — build 4
|
||
the title with animating wordmarks, build 5 the five-button main menu, builds
|
||
6/8/9 submenus, and the developer splash as the `palogo` bundle in the same pak.
|
||
* **Buttons are identifiable as data.** Element kind `0x3002` is a button, `0x0`
|
||
decoration, `0x10` a primitive; buttons sort top-to-bottom by resting Y; each
|
||
pairs with an `f`-suffixed highlighted variant.
|
||
* **The screen vocabulary.** The GamePart id table, 29 entries at `.rdata
|
||
0x820A1630`, confirmed by the executable's own factory-registration strings.
|
||
* **The resting pose rule** — the hold, not the longest dwell — and that a
|
||
keyframe is the *start of a ramp*.
|
||
* **Screen composition**, pixel-accurate for the tutorial pause menu and the title
|
||
main menu, via `sylpheed-cli screen render`.
|
||
* **The logo splash is not a video.** `logo1`–`logo4` are manifest-bound with no
|
||
`.wmv` on the disc; the splash is the RATC screen, which already renders.
|
||
|
||
## The open questions — these are the objective
|
||
|
||
Ordered by what blocks the port earliest. Each is done when its **gate** exists:
|
||
a written `docs/re/` result with the evidence, and reference data committed
|
||
alongside it.
|
||
|
||
| | Question | Gate |
|
||
|---|---|---|
|
||
| **Q1** | **What is a keyframe time?** Values run 16…269. 60 Hz frames would make the title intro ~4.5 s — plausible and untested. Also: is the ramp linear, or eased? | A measured answer against the running game, not an inference. Everything animated downstream depends on this number |
|
||
| **Q2** | **Which build is which screen state?** Confirm build↔state for splash, title/PRESS Ⓐ, main menu and each submenu | A table, each row confirmed against a capture of the real screen |
|
||
| **Q3** | **Paint order for these six screens.** Solved at runtime, unsolved from the file — the declaration table is provably not it | Either a rule derived from the bundle, or six measured orders and a clear statement that no file-side rule was found |
|
||
| **Q4** | **What does each button do?** Labels are baked into the sprites; no decoded field says which GamePart a button opens | Button → GamePart id, from code or from driving the game. Say which |
|
||
| **Q5** | **Navigation semantics.** Initial focus, wrap-around at the ends, whether left/right does anything, what B does on each screen | Observed behaviour, per screen |
|
||
| **Q6** | **The boot sequence, and what drives it.** Order is observable; the *data or code* that sequences it is not decoded. Include the attract loop and what returns to the title | The sequence, plus whatever the game reads to decide it |
|
||
| **Q7** | **Transitions.** What happens visually between screens — the `pteff00.prm` quads, a fade, a cut — and its timing | Described and timed against a capture |
|
||
| **Q8** | **Menu audio.** Which BGM per screen; which cue on move / confirm / back / error. The cue table is complete; the event binding is not | Cue names bound to events, with how you established each |
|
||
| **Q9** | **Video binding.** Which movie is the boot intro vs the new-game intro; whether playback is skippable and what ends it | Named movies plus the playback rules |
|
||
| **Q10** | ~~**What are a music bank's sub-waves?**~~ ✅ **ANSWERED — see below.** Every factual premise in the original row is refuted: a bank is **two** waves, not three (the 10 KB was the bank *header*, emitted by our own reader), and the three candidate roles it listed — intro + loop, two variations, two halves — are all dead. | ✅ **Gate met.** Role established on the menu's own bank: [`bgm-two-stems.md`](../re/structures/bgm-two-stems.md). 🟡 One sub-question survives — *which kind* of second stem — and it is 🟡 by measurement, not by neglect |
|
||
| **S1** | ~~**Ready Room probe.**~~ **DONE 2026-08-28 — [no-go](../re/ready-room-probe.md).** It is 2D and enumerates fine, but the pak is briefing/tactical-map content, not the Ready Room menu | ✅ go/no-go written |
|
||
|
||
## ✅ Emulator-side questions are NOT blocked — corrected 2026-08-30
|
||
|
||
⚠️ **This heading read "🔴 Emulator-side questions are blocked — the title is not
|
||
reachable here".** That is false and has been for some time: **twelve** emulator
|
||
runs on 2026-08-30 reached the settled title, gated on the plate pulse, and drove
|
||
it into the menu, `EXTRAS` and out of the archive. `HANDOFF.md` recorded the
|
||
banner as withdrawn; this document did not, and it is the one the brief says to
|
||
read **every iteration**.
|
||
|
||
🔴 **My first correction of this section was itself wrong, on all three clauses,
|
||
and is replaced (2026-08-30).** It read: *"The two items this section named are
|
||
UNBLOCKED, not answered … Both need a running menu, both now have one, and neither
|
||
has been attempted."* I wrote that without reading either page. Reading them:
|
||
|
||
* **`8AX` vs `ptbase` was RESOLVED on 2026-08-29.** Its page says so in its status
|
||
line — both its questions closed, kept for the evidence.
|
||
[8AX](../re/structures/ui-8ax-fullres-background.md)
|
||
* **The gamma control was attempted and half-answered**, and its page records that
|
||
the run *"needed the emulator only to **boot**, not to reach a menu … parked
|
||
behind the title-screen blocker for no reason."*
|
||
[tone curve](../re/structures/ui-render-tone-curve.md)
|
||
* **So neither ever needed a running menu**, and this section's premise was wrong
|
||
independently of whether the menu was reachable.
|
||
|
||
⚠️ **A correction is a new claim.** Mine replaced a stale status with an unchecked
|
||
one, in the same edit that criticised the document for carrying unchecked status —
|
||
which is the failure I had just finished cataloguing elsewhere.
|
||
|
||
The original section is kept below for the record, demoted so it cannot be read as
|
||
current.
|
||
|
||
**Status 2026-08-29, instrument-verified.** Two open items need a running menu:
|
||
the gamma control behind [tone curve](../re/structures/ui-render-tone-curve.md),
|
||
and separating `8AX` from `ptbase` in
|
||
[8AX](../re/structures/ui-8ax-fullres-background.md). Both are blocked.
|
||
|
||
✅ **The negative is now solid.** A probe that restarts its capture stream every
|
||
30 s and cross-checks itself against an independent grabber every 60 s
|
||
(9 checks, 8 agreeing to within 2 %) ran **560 continuous seconds from launch at
|
||
3.30 fps**: the interactive title's green Ⓐ plate never appeared, while the game
|
||
rendered throughout. The final frame correlates 0.0145 / −0.0047 / 0.0102 with
|
||
our title / main-menu / `EXTRAS` renders — attract-movie content.
|
||
|
||
❔ **Why is unknown.** `live-title-press-a.png` (753 glyph pixels) proves it was
|
||
reachable from this container on 2026-08-28. Clearing the shader cache fixed a
|
||
*black surface* but not this.
|
||
|
||
⚠️ Neither blocked item blocks the five menu screens. See
|
||
[capture-harness-status](../re/capture-harness-status.md) for the full trail,
|
||
including three earlier "the title never appears" claims that were withdrawn
|
||
because the instrument was broken each time.
|
||
|
||
## ✅ The Japanese-locale capture was taken — TWICE. Corrected 2026-08-30.
|
||
|
||
⚠️ **This heading read "🟡 Needs one more run — a Japanese-locale capture", and the
|
||
text below calls it "one capture we cannot take".** Both are false.
|
||
[`live-title-jp-at-rest.png`](../re/captures/title-builds/live-title-jp-at-rest.png)
|
||
and [`-run2`](../re/captures/title-builds/live-title-jp-at-rest-run2.png) are
|
||
committed, from two independent sessions, via
|
||
[`jp_title_session.sh`](../../tools/re-capture/jp_title_session.sh) which sets the
|
||
console language and always restores it.
|
||
|
||
📌 **And both questions it was blocking are closed** — Q1's association by the
|
||
[record-layout fix](../re/ui-keyframe-record-layout.md), and `rest()` for a
|
||
plateau-less element by a 1 036/1 036 discriminator, then **confirmed against this
|
||
very capture**: the corrected pose scores RMSE 41.69 where the stale one scores
|
||
58.41 ([`ui-resting-pose.md`](../re/structures/ui-resting-pose.md)).
|
||
|
||
⚠️ I noticed this section was stale several iterations ago, said so in a message,
|
||
and did not fix it. Kept below, demoted.
|
||
|
||
Recorded rather than worked around, per "do not improvise around a blocker".
|
||
|
||
Two questions have converged on needing **one capture we cannot take**: a running
|
||
capture of `GP_TITLE` **build 7**, the Japanese title screen.
|
||
|
||
* Q1's keyframe-time association. The shifted reading is favoured 26× by a
|
||
calibration-free measurement, and the only render it changes on the whole disc
|
||
is build 7 ([`ui-keyframe-time-unit.md`](../re/ui-keyframe-time-unit.md)).
|
||
* What `rest()` should return for a plateau-less element. The one element that
|
||
discriminates, `ptlogo_eff3.t32`, is also in build 7
|
||
([`structures/ui-resting-pose.md`](../re/structures/ui-resting-pose.md)).
|
||
|
||
**🔴 The "cannot" was wrong, and is withdrawn (2026-08-29).** It is true that
|
||
`user_language` is only ever `DECLARE_int32` here, with no `DEFINE` and no entry
|
||
in `xenia-canary.config.toml`, so there is no flag to pass — and passing an
|
||
unknown one is specifically dangerous, because `run-canary`'s own header records
|
||
that xenia calls `ShowSimpleMessageBox` from `ParseLaunchArguments` *before*
|
||
logging starts. But the cvar is not the only route, and I stopped at the first
|
||
one I checked.
|
||
|
||
**The language is persisted, and canary's own file is writable.**
|
||
`kernel_state.cc` builds `XConfig` over `<storage_root>/xconfig.settings`, which
|
||
exists here at `/sylph-home/re/.local/share/Xenia/xconfig.settings` (6 680 B).
|
||
`user.language` is a **big-endian u32 at file offset `0x912`**, located by three
|
||
independent landmarks rather than guessed:
|
||
|
||
| landmark | expected | found |
|
||
|---|---|---|
|
||
| `music_volume` (`User+449`) | `0.7f` | BE float at 2727 → `User` base `0x8e6` |
|
||
| `language` (`User+44`) | `1` = `kEnglish`, the hard-coded default | **1** at `0x912` |
|
||
| `country` (`User+64`) | United States | `103` at `0x926` |
|
||
|
||
`XLanguage::kJapanese = 2` (`xbox.h:307`). Writing 2 there and restoring
|
||
afterwards is using canary's own persistence, not patching its code.
|
||
|
||
**🔴 DIAGNOSED 2026-08-29 — the harness, not the game.** `screenshot` costs
|
||
**10.8 s while xenia is running** and **0.117 s once it is killed** (92×, at a
|
||
1-minute load average of 1.80, so it is contention with the emulator). A
|
||
two-grab polling loop therefore samples every **~41 s**, against a title screen
|
||
this corpus documents as lasting *a few seconds*. Four runs — two locales, two
|
||
launch paths, two gamma settings — were all blinking slower than the event.
|
||
🔴 **…and that diagnosis is itself refuted (same day).** The faster probe was
|
||
built — 3.98 s → **0.29 s** per sample, 13.7×, control-verified at 753/327 — and
|
||
at 3.99 fps for **420 continuous seconds (1 674 samples)** the interactive title
|
||
*still* never appeared. Sampling rate was a real defect and not the cause.
|
||
✅ So "the interactive title does not appear mid-run without a pad press" is
|
||
reinstated, now as a dense measurement. ⚠️ Its reach is a **mid-run** window; it
|
||
says nothing about the boot title.
|
||
🟡 Leading hypothesis, unconfirmed: the plate appears only in the **boot** title
|
||
window and the attract loop's title has none — which is precisely what
|
||
`title_states_capture.sh` was written to test. The experiment is to start the
|
||
fast probe from t=0, not attach to a run already in progress.
|
||
See [capture-harness-status](../re/capture-harness-status.md).
|
||
|
||
**🟡 Still not settled — two runs, and the reason moved again.**
|
||
|
||
*Run 1 (2026-08-28)* reported "title not seen" in 787 s. **That was a broken
|
||
tool, not the game.** `wait_title.sh` still carried the single-pixel oracle that
|
||
`is_title.py` was written to replace — a 1280×720 coordinate sampled against the
|
||
1279×675 game surface, so it always reads the copyright line. Fixed; it now
|
||
delegates to `is_title.py`.
|
||
|
||
*Run 2 (2026-08-29)*, with the working oracle and the profile flag the English
|
||
captures use, **still did not reach the interactive title.** Not one frame in the
|
||
run showed a single green-Ⓐ glyph pixel, and content correlation against either
|
||
build-7 render never exceeded **0.22**. The game sat in the attract movie
|
||
throughout, polling `XamInputGetKeystrokeEx` (601 calls) — i.e. alive and waiting
|
||
for input, not hung.
|
||
|
||
**✅ The control was run (2026-08-29), and it removes the locale from the
|
||
picture.** Same flags, same oracle, English locale: **75 samples over 734 s, every
|
||
one glyph = 0.** The English boot does not present the interactive title either.
|
||
Canary was alive throughout and polling `XamInputGetKeystrokeEx` (1 801 calls);
|
||
the frame at 734 s has content but correlates only **0.15** with build 4's render
|
||
and 0.05 with the main menu, i.e. it is an attract-movie frame, not title art.
|
||
|
||
So the Japanese run was **not** failing because of the locale — neither locale
|
||
reaches the interactive title in ~12 minutes of no pad input. **🔴 Tried the proven path too, and I am stopping this line (2026-08-29).**
|
||
Run 3 launched exactly the way `boot_menu.sh` does — `DISPLAY=:98`, `--apu=sdl`,
|
||
`/dev/shm/xenia_*` cleared, the existing profile signed in — and drove
|
||
`skip_intro.sh`, the detector that is documented to work. It classified **every
|
||
one of 20 samples over 604 s as "movie"** and waited them all out, then timed
|
||
out. A direct check at 604 s confirms it was right: zero green-glyph pixels,
|
||
`screen_id` = other, warm mean (69,53,40), correlation **0.09** with build 7. The
|
||
game really was playing attract movies for ten minutes.
|
||
|
||
So: **three runs, two locales, two launch paths, ~35 minutes of emulator time, no
|
||
interactive title.** Either the attract loop is far longer than the 600–780 s
|
||
windows tried, or something has regressed since `live-title-press-a.png` was
|
||
captured (that one came from a pad-driven boot — commit `88b3ce9`, "booting to
|
||
the main menu and walking it").
|
||
|
||
⚠️ **Not worth more loop iterations.** This capture does not block any of the
|
||
five menu screens, and it has now cost five. It is written down so a later
|
||
session with a reason to spend an hour on the boot path can pick it up; the
|
||
locale mechanism, the fixed oracle and the launch recipe are all in place, so
|
||
what is left is patience with the attract loop, not tooling.
|
||
|
||
The locale is restored to English; `set_console_language.py ja` flips it back in
|
||
one command.
|
||
|
||
⚠️ Neither question blocks the five menu screens. Q1's *interpolation law* is
|
||
settled and only multi-keyframe absolute timing is open; `rest()` differs from
|
||
its alternative on **one** element across all five screens, and the current
|
||
answer there is the defensible one.
|
||
|
||
## ✅ Rotation — the decision was TAKEN and implemented. Corrected 2026-08-30.
|
||
|
||
⚠️ **This heading read "🔵 Needs a human decision — rotation (raised 2026-08-29)".**
|
||
It was answered the same day it was raised: `HANDOFF.md` records *"OPTION A IS
|
||
DONE. The reference renderer rotates"*, and `ui_layout.rs` carries the rotated blit
|
||
with a control test (`rotation_control_known_angles`) pinning it against angles
|
||
whose answer is arithmetic — 0° and 360° byte-identical to the unrotated path.
|
||
|
||
✅ **And the field itself is now confirmed from the ORACLE, not just decoded.** The
|
||
port's `rotation_deg` of **+30** on `pteff03` and **−45** on `pteff03a` predict a
|
||
rotated quad's AABB height at 1135.3 and 1301.1; the game's draw stream measures
|
||
**1134** and **1303** — both under 0.2 %
|
||
([`data/title-sweep-drawn-at-rest.txt`](../re/data/title-sweep-drawn-at-rest.txt)).
|
||
|
||
The original section is kept below, demoted.
|
||
|
||
The port agent asks whether it should **render** `rotation_deg` (decoded at
|
||
keyframe `+12`) when `sylpheed-cli screen render` deliberately does not. Its own
|
||
framing is the reason this is not mine to settle: if the port rotates and the
|
||
reference renderer does not, then `verify-screen` reports a large title diff that
|
||
means *"the port is right"* — a silently inverted signal.
|
||
|
||
The RE half is answered and is in HANDOFF: rotation is about the **declared
|
||
pivot** (measured against a GPU capture, not assumed), and it changes nothing on
|
||
the five screens **at rest**.
|
||
|
||
What needs a decision is which way the divergence gets closed:
|
||
|
||
* teach `ui_layout::blit` a rotating path, so the two renderers stay comparable
|
||
and the diff keeps meaning "someone is wrong" — costs work in the reference
|
||
renderer, which is otherwise not on the port's critical path; or
|
||
* let the port render rotation and mark the title as a known-divergent screen in
|
||
`verify-screen`, accepting a check that no longer guards the title.
|
||
|
||
Recorded rather than chosen, per "do not improvise around a blocker".
|
||
|
||
## ✅ Q10 is answered — corrected 2026-08-30
|
||
|
||
The Q10 row above was written on a premise that has since been **refuted in every
|
||
part**, and it survived as a live question for days after the refutation landed.
|
||
Recording the correction here rather than silently editing the row:
|
||
|
||
* ❌ *"`BGM_001.slb` is three sub-waves (10 KB, 4.47 MB, 4.67 MB)"* — the 10 KB is
|
||
the **bank header**. Our reader emitted it, from a modulus valid only for a
|
||
header shorter than one XMA packet. A bank is **two** waves, **28/28** disc-wide.
|
||
* ❌ *"we currently concatenate them blindly"* — and concatenating is **wrong**,
|
||
now measured: the two waves are **sample-synchronous** and the running game
|
||
decodes **both at once** (the XMA probe at the main menu saw two stereo streams
|
||
whose byte sizes are `BGM_103`'s two declared waves, exactly).
|
||
* ❌ *"could be intro + loop, or two variations, or two halves of one piece"* — all
|
||
three predict unequal durations; **32 banks give equal ones**.
|
||
* ✅ **The gate — "the role of each sub-wave, established for at least the menu
|
||
BGM" — is met**, and on the menu's own bank: `BGM_103`, named from the
|
||
executable, confirmed against the disc and against the running game.
|
||
|
||
🟡 **What is still open is narrower than the row**: *which kind* of second stem —
|
||
the rear pair of a 4-channel mix, or a second intensity layer. Both predict
|
||
simultaneity, so runtime observation cannot separate them, and a coherence
|
||
discriminator run 2026-08-30 **refuted the "filtered copy" model but could not
|
||
separate the two** — its own control showed that in this material even L vs R of
|
||
one performance reads only 0.22–0.50, so the test's premise does not hold.
|
||
[`../re/data/bgm-stem-coherence.txt`](../re/data/bgm-stem-coherence.txt)
|
||
|
||
⚠️ **This distinction does not block the port.** Both readings give the same
|
||
instruction: play both waves, aligned at sample 0, together. It changes only how
|
||
they would be *mixed* if the port ever does surround.
|
||
|
||
## Known unknowns — say so, do not fill them in
|
||
|
||
Some of these may turn out to be undecodable. That is a valid, useful answer, and
|
||
it is better than a guess, because the port agent will otherwise have to author
|
||
the mapping by hand and needs to know it is authoring rather than transcribing.
|
||
|
||
For each question, the answer is one of:
|
||
|
||
* **decoded** — here is the field, here is the disc-wide check;
|
||
* **measured** — not on the disc in any form we found, but here is what the
|
||
running game does, and here is the capture;
|
||
* **undecodable, with reach** — we looked here, here and here, and this is why it
|
||
is not there.
|
||
|
||
Never a fourth thing. In particular: if Q4 ends as "read the labels off the sprite
|
||
images by eye", say exactly that — it is then an authored mapping on the port
|
||
side, not a disc fact, and mislabelling it would put a guess into the port wearing
|
||
the badge of a measurement.
|
||
|
||
## The Ready Room probe (S1) — one iteration, then stop
|
||
|
||
`GP_READY_ROOM.pak` is the largest UI archive on the disc, 1 106 entries, and only
|
||
**6 of its names resolve**. It is also ISL-scripted. That is either a week or a
|
||
quarter, and one cheap test tells you which.
|
||
|
||
Our screen catalog enumerates bundles by **content**, not by name — `is_build` /
|
||
`is_composable` read the bytes — so unrecoverable *paths* do not necessarily mean
|
||
unrenderable *screens*.
|
||
|
||
```bash
|
||
sylpheed-cli screen list "$SYLPHEED_DISC/dat/GP_READY_ROOM.pak"
|
||
sylpheed-cli screen render --build <n> "$SYLPHEED_DISC/dat/GP_READY_ROOM.pak" /tmp/rr.png
|
||
```
|
||
|
||
Report how many builds it finds, whether any composite looks like a Ready Room,
|
||
and **whether the room is 2D at all** or 3D with a UI overlay — if it is 3D the
|
||
answer is no-go by definition, not "try harder".
|
||
|
||
**Then stop and write the go/no-go.** Do not start Ready Room work on your own
|
||
authority.
|
||
|
||
## Publishing a decoder state the port pins
|
||
|
||
The port's exporter depends on `sylpheed-formats` **by revision**, so a commit of
|
||
yours becomes part of its build. That creates an obligation that is easy to miss:
|
||
|
||
⚠️ **A commit reachable only from an `auto/*` branch is not safe to pin.** If the
|
||
branch is later deleted, or — worse — **squash-merged**, the object is orphaned.
|
||
Squash creates *new* commits, so `main` appears to contain the work while the
|
||
pinned sha becomes unreachable, and the port stops building for anyone doing a
|
||
fresh checkout. Silently, at their build, long after the moment of breakage.
|
||
|
||
**So when you land something the port needs, tag it:**
|
||
|
||
```bash
|
||
git tag -a formats-pin-$(date +%F) -m "what the port gets from this state"
|
||
push-work # --follow-tags publishes annotated tags with the branch
|
||
```
|
||
|
||
A tag is a permanent ref. It survives branch deletion and squash-merge, it is
|
||
self-documenting in the port's `Cargo.toml`, and it fails loudly at *fetch* if it
|
||
ever goes missing rather than silently at build.
|
||
|
||
Then tell the port over the message channel that a new pin exists. It bumps
|
||
deliberately, as its own commit — that is how it stays current without floating,
|
||
which would only give it staleness it cannot see.
|
||
|
||
`formats-pin-2026-08-29` at `7eeae30` is the first, created because the port had
|
||
already pinned a commit that lived on one topic branch and nowhere else.
|
||
|
||
## Handing it over
|
||
|
||
[`HANDOFF.md`](HANDOFF.md) is the single page the port agent reads. Keep it
|
||
current as you answer questions: it is a summary with links into `docs/re/`, not a
|
||
second copy of the findings. An answer that is not reachable from HANDOFF.md has
|
||
not been delivered.
|
||
|
||
## Out of scope
|
||
|
||
3D, gameplay, HUD, missions, save/load, localisation beyond English, the Godot
|
||
project itself, any asset pipeline, and any archive outside `GP_TITLE`,
|
||
`tables.pak`, `sound.pak` and `dat/movie/` — except for the S1 probe.
|