Project Sylpheed ships as a retail XEX2 (human decision, 2026-09-16), so Phase 2's key harvest is closed rather than left looking unfinished. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
674 lines
34 KiB
Markdown
674 lines
34 KiB
Markdown
# Consolidating six repositories into two
|
||
|
||
**Set by the human, 2026-09-13.** The end state is **two** repositories:
|
||
|
||
| repo | holds |
|
||
|---|---|
|
||
| **`Sylpheed`** | everything — the port, the RE corpus, every tool |
|
||
| **`Xenia-Canary`** (our fork) | the emulator and oracle, with our instrumentation |
|
||
|
||
Everything else is retired. This page is the ordered **how**, with a gate after
|
||
each phase, and a ledger of what was checked rather than assumed.
|
||
|
||
⚠️ **Most paths on this page do not resolve in this repository, on purpose.**
|
||
They name files in `xenia-rs`, `xex2tractor`, `xenia-canary` and the project
|
||
root — the things being moved *in*. A citation checker pointed at
|
||
`docs/agents/` will flag every one of them until the phases below are done, and
|
||
that is the correct reading: **each unresolved path here is work outstanding.**
|
||
|
||
📌 Two decisions from the human frame the whole plan:
|
||
|
||
> *"The emulator is dead and can be dropped altogether. But the DuckDB generator
|
||
> should still be a useful tool… We probably can move it into a standalone tool
|
||
> under Sylpheed."*
|
||
|
||
> *"xex2tractor was an early attempt and is long dead too… you may want to scan
|
||
> and compare the tools for anything that is in xex2tractor only."*
|
||
|
||
---
|
||
|
||
## ✅ Closed 2026-09-16
|
||
|
||
**The end state is reached: two live repositories.** `Syplheed-Reborn`,
|
||
`Sylpheed-Godot`, `xenia-rs` and `xex2tractor` are archived read-only on Gitea
|
||
(by the human, 2026-09-16), not deleted. The harvests are on `main`: the DuckDB
|
||
tool (`crates/sylpheed-xexdb`, #32), the import-thunk naming (#35, re-landed as
|
||
#39), the xex2tractor reference files (#30) and the orphan assets (#34). The last
|
||
reference files from the project root follow in #46. `sylpheed.db` lives in this
|
||
repository's root and was rebuilt from scratch.
|
||
|
||
| phase | outcome |
|
||
|---|---|
|
||
| **5** archive | ✅ the four repos archived; `xenia-rs` and `sylpheed-reborn` clones deleted from this machine (the other two never had one here) |
|
||
| **7** drops | ✅ `Sylpheed/target/` deleted; `agent-backups/`, `stock-oracle/`, `texcompare/`, `ship_render/`, the `pi/*` branches and the root Canary logs are not on this machine |
|
||
| fork | ✅ Canary's default branch is `sylpheed-re`, no longer `xenia-rs` |
|
||
|
||
Still the human's, unchanged: the **retention** question (all repositories are
|
||
public; `docs/re/captures/` holds game screenshots) and the **history rewrite**,
|
||
recommended against in Phase 5.
|
||
|
||
The rest of this page is the working record as it stood during the move. Its
|
||
paths into the retired repositories no longer resolve because those clones are
|
||
gone, not because work is outstanding.
|
||
|
||
## Where things stand
|
||
|
||
| repo | server | local tree | tracked | disposition |
|
||
|---|---|---|---|---|
|
||
| **Sylpheed** | 598 MB | 33 GB | 131 MB / 1470 files | the target |
|
||
| **Xenia-Canary** | 71 MB | 20 GB | — | the keeper — ⚠️ **dirty** |
|
||
| Syplheed-Reborn | 103 MB | 1.4 GB | 100 MB / 787 files | ✅ absorbed → archive |
|
||
| Sylpheed-Godot | 0.4 MB | 99 MB | 0.3 MB / 39 files | ✅ absorbed → archive |
|
||
| xenia-rs | 9 MB | 1.2 GB | 61 MB | harvest the DB tool → archive |
|
||
| xex2tractor | 0.3 MB | *no clone* | — | harvest four things → archive |
|
||
|
||
`Sylpheed` has **two root commits** — `feat: initialise workspace` (2026-03-25,
|
||
Reborn's) and `scaffold the Godot port` (2026-08-28). Both histories were
|
||
grafted in, not copied, which is why the absorption checks below pass.
|
||
|
||
## The ledger — what was verified, and how
|
||
|
||
Nothing here is inference. Each row is a command that was run on 2026-09-13.
|
||
|
||
| claim | method | result |
|
||
|---|---|---|
|
||
| Reborn fully absorbed | all **21 remote + 8 local** branch tips vs `Sylpheed/main` | every one an ancestor |
|
||
| Sylpheed-Godot absorbed | both branch tips, + the root-commit graft | every one an ancestor |
|
||
| `agent-backups/` redundant | `git bundle list-heads`, 23 refs across 4 bundles | all present in Sylpheed |
|
||
| 3 unpushed `pi/*` branches redundant | `git patch-id --stable`, per commit | all 4 patches on `main` |
|
||
| nothing lives outside git | issues / PRs / releases / wikis, all six repos | **0** everywhere but Sylpheed |
|
||
| the DB tool is separable | `git grep "use xenia_cpu::"` in `xenia-analysis` | **three symbols** |
|
||
|
||
### 🔴 What the ledger found that a naive plan would have destroyed
|
||
|
||
1. **The F1 instrument exists in one place, uncommitted.** `xenia-canary` has two
|
||
dirty files — `hid/file/file_input_driver.h` (`--pad_file_repeat`) and
|
||
`app/xenia_main.cc` — plus **4 unpushed commits** on `sylpheed-re` and a
|
||
branch, `audit-handle-lifecycle-probes`, **contained in no remote at all**.
|
||
That patch is the instrument behind `REPEAT_DELAY`/`REPEAT_INTERVAL`, now
|
||
shipped in the port. Its own finding page says *"not yet upstreamed."*
|
||
2. **xenia-rs has 4 unpushed branches** — `iterate-4B/ui-perf`, `4C/jit`,
|
||
`4D/parallel`, `4E/native-threads`. Dropping the emulator is a decision;
|
||
dropping work that was never backed up is a different act.
|
||
3. **The tool is not on `master`.** `xenia-rs/master` last moved **2026-06-15**;
|
||
the DuckDB and disassembler work is on `origin/iterate-4A/apu-xma-stage1`
|
||
(**2026-09-10**, +22 commits). Extract from the default branch and you get a
|
||
three-month-old tool.
|
||
|
||
---
|
||
|
||
## Phase 0 — Secure the single-copy work ✅ **DONE**
|
||
|
||
**Before touching anything else.** One `git checkout` in `xenia-canary` ends the
|
||
reproducibility of a measurement that is already live in the port.
|
||
|
||
1. Commit `file_input_driver.h` + `xenia_main.cc` on `sylpheed-re`.
|
||
2. Push `sylpheed-re` (4 + 1 commits) and `audit-handle-lifecycle-probes`.
|
||
3. ~~Resolve the `third_party/DirectXShaderCompiler` submodule drift.~~
|
||
✅ **There is none.** `git submodule status` shows no `+`: the checked-out
|
||
commit matches the recorded one. The ` m` in `git status` is *nested*
|
||
submodule dirt — DXC's own `external/SPIRV-Headers` and `SPIRV-Tools` — which
|
||
is upstream's business and must not be committed here.
|
||
|
||
**Gate — and the first version of it was wrong.** It compared each local branch
|
||
to a remote **by SHA**, and reported seven safe branches as unpushed: a branch
|
||
that is an *ancestor* of a pushed branch is already preserved. A check that goes
|
||
red for something unactionable is the failure this repo keeps naming, so the
|
||
gate tests what it means — **reachability**:
|
||
|
||
```bash
|
||
git for-each-ref --format='%(refname:short)' refs/heads | while read b; do
|
||
s=$(git rev-parse "$b"); found=""
|
||
for r in $(git for-each-ref --format='%(refname:short)' refs/remotes); do
|
||
git merge-base --is-ancestor "$s" "$r" 2>/dev/null && { found=$r; break; }
|
||
done
|
||
[ -n "$found" ] || echo "🔴 $b NOT REACHABLE from any remote"
|
||
done
|
||
```
|
||
|
||
✅ **PASSED 2026-09-13** — `8e63a9542` commits the instrument; `sylpheed-re` and
|
||
`audit-handle-lifecycle-probes` pushed; all **14** local branches reachable.
|
||
|
||
## Phase 1 — Make the archive honest ✅ **DONE**
|
||
|
||
Push xenia-rs `iterate-4B/ui-perf`, `4C/jit`, `4D/parallel`, `4E/native-threads`.
|
||
|
||
They are being retired, not rescued. But a retired branch that was never pushed
|
||
is not retired — it is deleted, quietly, by someone who thought it was safe.
|
||
|
||
**Gate:** the same loop, run in every repo, prints nothing.
|
||
|
||
✅ **PASSED 2026-09-13** — all four pushed (32 / 52 / 56 / 59 commits ahead of
|
||
`master`). The loop is clean in all five repos **except** `pi/clippy`,
|
||
`pi/clippy-clean` and `pi/reauth3` in Sylpheed, which are unreachable by
|
||
ancestry and redundant by *patch-id* — the ledger's reason, and Phase 7's
|
||
disposal. The gate is right to flag them; they are the one case where the
|
||
answer lives outside it.
|
||
|
||
## Phase 2 — Harvest xex2tractor, then retire it ✅ **DONE (PR #30)**
|
||
|
||
Four things exist only there.
|
||
|
||
| take | why |
|
||
|---|---|
|
||
| ~~**XEX2 devkit + XEX1 retail master keys**~~ | `xenia-xex` hardcodes retail only; devkit is present but `#[allow(dead_code)]` and XEX1 is absent. xex2tractor tries all three in a validating loop. ✅ **Not taken, by decision (human, 2026-09-16):** Project Sylpheed is a retail XEX2, so the retail key is the only one needed — the dead devkit key was removed from `sylpheed-xex` instead. |
|
||
| ~~**`extract -r`**~~ | 🔴 **RUN, AND THE INFERENCE WAS WRONG** — see the gate below. Optional, and dangerous if it is ever mistaken for the `.pe`. |
|
||
| **`doc/xex2_format.md`** (39 KB) and **`doc/xbox360_exports.json`** (938 KB, 2,913 exports across xboxkrnl / xam / xbdm) | byte-identical to untracked loose copies in the project root. Tracked by **no repo**. |
|
||
| **`LICENSE`** (MIT) | Sylpheed has none. |
|
||
|
||
Leave everything else: `xenia-xex` is ahead on `resources.rs` (XDBF/XACH),
|
||
`pdata.rs`, `tls.rs`, basic zero-fill compression, and the analysis layer.
|
||
|
||
**Gate — ✅ RUN 2026-09-13, and it corrected this page in two directions.**
|
||
|
||
```
|
||
extract (xex2tractor, no -r) ✅ IDENTICAL to the project .pe
|
||
extract -r (xex2tractor) ❌ differs — 1,903 bytes over 273 runs
|
||
extract (xenia-rs, iterate-4A) ✅ IDENTICAL — and .xex.json identical too
|
||
```
|
||
|
||
**Right:** the `.pe` *is* an xex2tractor extract. Hash-for-hash.
|
||
|
||
**Wrong:** it was made **without** `-r`. The project's `.pe` is the plain
|
||
decrypted-and-decompressed image, imports **unresolved** — and `-r`'s 1,903
|
||
differing bytes sit in the import thunk region from `VA 0x82000600`.
|
||
|
||
🔴 **So the dependency this gate existed to protect does not exist.** This page
|
||
claimed *"drop this and the file every `.pe`-offset citation depends on can
|
||
never be rebuilt."* It can: **`xenia-rs extract`, the tool Phase 3 keeps,
|
||
reproduces it byte-for-byte** — along with the loose `.xex.json`, which turns out
|
||
to have come from xenia-rs, not xex2tractor.
|
||
|
||
⚠️ **And `-r` must never become the `.pe`.** It writes into the image, so a `.pe`
|
||
built with it would silently invalidate every byte-offset citation in
|
||
`docs/re/`. If resolved-import disassembly is ever wanted, it is a **separate
|
||
artifact under a different name**, never a replacement.
|
||
|
||
That leaves xex2tractor's unique list at three items, none urgent — the two
|
||
master keys, the two doc assets, and the licence. **The doc assets are now the
|
||
only reason to visit that repository.**
|
||
|
||
📌 Learned in passing: `iterate-4A` **builds clean** (`xenia-app`, one unused
|
||
variable warning), so Phase 3's source is not bit-rotted — and it needs
|
||
`libavutil-dev` and friends via `ffmpeg-sys-next`, dragged in by the emulator's
|
||
XMA audio. The standalone tool sheds a C library the CI image did not have.
|
||
|
||
## Phase 3 — Lift the DB tool out of the emulator ✅ **DONE (PR #32)**
|
||
|
||
From `origin/iterate-4A/apu-xma-stage1`, **never** from `master`.
|
||
|
||
```
|
||
xenia-analysis 10,658 db, vtables, jumptables, func, xref, rtti,
|
||
strings, xdbf, static_init, demangle, sql_views
|
||
disasm slice of cpu 3,681 disasm.rs + decoder.rs + opcode.rs (of 21,813)
|
||
xenia-xex 2,018 header, loader, lzx, pe, pdata, tls, resources
|
||
xenia-vfs 444 so it reads an ISO, not just a loose .xex
|
||
xenia-types 361
|
||
───────
|
||
≈17,200 of ~71,000
|
||
```
|
||
|
||
The coupling is exactly three symbols — `decoder::decode`, `disasm::DisasmItem`,
|
||
`disasm::format`. Interpreter, JIT, scheduler, VMX, block cache, reservations:
|
||
none of it is reachable from the generator.
|
||
|
||
`xenia-xex` declares `xenia-memory` and `xenia-types` as dependencies and has
|
||
**zero `use`** of either. Probably droppable — confirm by compiling, do not
|
||
assume.
|
||
|
||
Travelling with it: `zq.py`, `tests/db_schema_golden.rs`,
|
||
`tests/disasm_goldens.rs`, `examples/decode_table_check.rs`.
|
||
|
||
**Two corrections to make during the move, not after:**
|
||
|
||
* the CLI help says **"SQLite database"** in two places. It is **DuckDB**
|
||
(`duckdb = { workspace = true }`).
|
||
* `zq.py`'s documented escape hatch — *"the engine vtable / rdata is NOT in the
|
||
DB … read it from guest memory with `xenia-rs exec --dump-addr`"* — **dies with
|
||
the emulator.** Replace it with Canary's dump or a direct read of the `.pe` at
|
||
`VA − 0x82000000`, and say which, in the file.
|
||
|
||
**Gate — ✅ PASSED 2026-09-13.** Schema a **superset** (22 new tables, none
|
||
lost). `instructions` 1,865,751, `pdata_entries`, `imports`, `sections` and all
|
||
three `eh_*` identical to the row; most others slightly higher. Two tables drop
|
||
~1.8 M and it is not a loss — the xref delta is **entirely `ind_call`**
|
||
(everything else nets **+1,512**), and the new schema records why: **6,556 sites
|
||
`truncated=true` carrying 1,801,075 candidates** against 427 untruncated
|
||
producing 4,199 rows. The old database materialised a cross product.
|
||
|
||
## Phase 4 — Prune the captures, and give `docs/re/` the check `docs/port/` has ✅ **DONE (PR #33)**
|
||
|
||
**`docs/re/captures/` is 257 files / 117.6 MB, and it is not junk.** Measured:
|
||
|
||
```
|
||
193 (81.2 MB) cited by a docs/re page, or opened by a tool
|
||
64 (36.5 MB) referenced by nothing
|
||
```
|
||
|
||
🔴 **Two are test fixtures and `cargo test` opens them:**
|
||
|
||
```
|
||
crates/sylpheed-formats/tests/ui_paint_order_disc.rs → captures/title-screen-oracle.png
|
||
crates/sylpheed-formats/tests/unit_layout_disc.rs → captures/stage02-live-unit-definitions-deep.txt
|
||
```
|
||
|
||
Sixteen more tools under `tools/port/` and `tools/re-capture/` read from there.
|
||
|
||
**Do:**
|
||
|
||
1. ✅ **DONE — `tools/re/check-capture-citations`**, the mirror of
|
||
`tools/port/check-citations`, which has only ever checked `docs/port/*.md`.
|
||
It reports two classes — a capture nothing cites, and a page citing a capture
|
||
that was never committed. It needs a `--selftest` that plants one of each, or
|
||
it is not a check.
|
||
2. ✅ **DONE — and there was ~~**10**~~ ONE.** Eight of the ten were *directory*
|
||
references that resolve; one was sentence punctuation in the regex. The real
|
||
one, `live-submenu-unidentified.png`, had been **committed and then deleted**
|
||
on 2026-08-30 while the page kept citing it and kept making the claim it
|
||
backs. Restored, not de-linked.
|
||
3. ✅ **DONE — 46, not 64.** Two are opened by *filename* from
|
||
`screen_match.py`, and sixteen sit inside directories that pages cite as
|
||
directories. 30.7 MB out of the checkout.
|
||
|
||
⚠️ **An orphan is not automatically waste.** It may be evidence a page *should*
|
||
have cited, and the page is the thing that is wrong. Fix the citations before
|
||
deleting, or step 3 silently ratifies every omission in step 2.
|
||
|
||
📌 **Going forward the class that matters is not captures-vs-code:**
|
||
|
||
| | |
|
||
|---|---|
|
||
| **evidence** — a screenshot a finding points at | belongs beside the finding, as the convention says |
|
||
| **run output** — `.jsonl`, `.log`, `.csv` | the same class as xenia-rs's `audit-runs/`, which this plan drops |
|
||
|
||
The two `.jsonl` mission-state dumps alone are 4.3 MB, and one is an orphan.
|
||
|
||
## 🔴 A gate that walks `refs/heads` cannot see a working tree
|
||
|
||
**Found 2026-09-14, on the other machine, while pulling the repos.** `xenia-rs`
|
||
had **468 uncommitted lines plus an untracked 338-line `imports.rs`** sitting in
|
||
`crates/xenia-analysis` — the very crate Phase 3 lifted. Written 2026-09-10
|
||
21:01–21:23; found four days later.
|
||
|
||
It existed in exactly one place, and that place was not git:
|
||
|
||
| | `imports.rs` | `import_address` | `zq.py impcalls` |
|
||
|---|---|---|---|
|
||
| PR #32, the lifted crate | ✗ | ✗ | ✗ |
|
||
| `iterate-4A` (committed) | ✗ | ✗ | ✗ |
|
||
| **xenia-rs working tree** | **✓** | **✓ (4 files)** | **✓** |
|
||
|
||
**Phase 0 is titled "Secure the single-copy work" and its gate passed anyway**,
|
||
because the gate tests branch *reachability* — it iterates `refs/heads`. A dirty
|
||
working tree has no ref. Canary's two dirty files were secured only because a
|
||
human already knew they were there; nothing *found* them. Phase 1 then pushed
|
||
xenia-rs's four branches and called the archive honest while this sat beside them.
|
||
|
||
Phase 5 ends with *"delete the local clone"* and Phase 7 drops the emulator, so
|
||
this had a deletion scheduled against it.
|
||
|
||
**The gate needs a second half. Before archiving or deleting anything:**
|
||
|
||
```bash
|
||
for d in Sylpheed sylpheed-reborn xenia-rs xenia-canary; do
|
||
n=$(git -C "$d" status --porcelain | wc -l)
|
||
[ "$n" = 0 ] || echo "🔴 $d has $n uncommitted path(s)"
|
||
done
|
||
```
|
||
|
||
Run 2026-09-14 across all four: only `xenia-canary` is dirty, and both entries
|
||
(`build-cross/`, `vkd3d-proton.cache`) are already on Phase 7's drop list.
|
||
|
||
✅ **Secured** — `xenia-rs` branch **`harvest/import-thunk-naming`** @ `b4f19f1`,
|
||
pushed. Committed exactly as found, not cleaned up: editing it first would have
|
||
destroyed the thing being preserved. `cargo check -p xenia-analysis` = **0**.
|
||
|
||
✅ **Ported** — **PR (new)** `feat/xexdb-import-naming`, stacked on #32. The
|
||
lift's base is exactly the harvest's parent, so every file merged three-way:
|
||
**10 conflicts, all of them caused by the lift's reformatting pass** — 9 are the
|
||
harvest's additions landing inside blocks rustfmt had rewrapped (so the content
|
||
is kept and re-indented to the lift's style), and **1 is genuinely semantic**:
|
||
insertion *order*, because `xdbf_achievements.image_id` is now an FK onto
|
||
`xdbf_images(id)`, so the image rows must be inserted first. The conflict there
|
||
was the stale copy of the block left at its old position. `cargo test -p sylpheed-xexdb` 10/0, clippy and fmt clean.
|
||
|
||
📌 Two things it carries, both absent from #32 as lifted: import-thunk
|
||
recognition (disassembly says `xboxkrnl.exe::RtlEnterCriticalSection` instead of
|
||
`.long 0x01010194`), and **16 schema-wide foreign keys** — with `functions`
|
||
deliberately excluded and the golden test now *asserting* zero inbound FKs onto
|
||
it, since DuckDB's UPDATE is delete+insert and one inbound FK would make
|
||
`functions.name` un-updatable and break `apply_re_symbols.sql`.
|
||
|
||
### Phase 3 residue found while porting
|
||
|
||
* ✅ **`tools/zq.py` hardcoded `/home/fabi/…/xenia-rs/sylpheed.db`** — a default
|
||
path *inside the repository Phase 5 archives and Phase 7 drops*, resolving on
|
||
exactly one machine and days from becoming a `duckdb` exception with nothing
|
||
saying why. **Fixed in PR #35 (`38cc170`):** `$SYLPH_XEXDB` → `<repo
|
||
root>/sylpheed.db` → a refusal naming both, with the repo-root path taken
|
||
relative to the *script* rather than the caller's cwd. **No fallback beyond
|
||
that, deliberately** — the database is an untracked build artefact of a few
|
||
hundred MB, so there is nothing to fall back *to*, and a default that silently
|
||
resolves to the wrong database is worse than no default. Same shape as #16.
|
||
The stale `xenia-rs dis` regeneration hint is now `sylph-xexdb dis`.
|
||
* `SCHEMA.md` is still headed *"`xenia-analysis` schema reference"* and cites
|
||
`xenia-rs dis --db` — the retired repo and binary. Not fixed.
|
||
* Phase 3's "says SQLite, is DuckDB" correction **was** made where it counts
|
||
(README and the binary's module doc both say DuckDB), but two inline comments
|
||
at `sylph-xexdb.rs:499` and `:874` still say SQLite.
|
||
|
||
## ▶️ The Phase 5 and Phase 7 checks, re-run 2026-09-14 on the second machine
|
||
|
||
This page says *"Do not trust this page's ledger when you get there."* So the
|
||
checks were re-run rather than read. **No repository was archived and nothing was
|
||
deleted** — both are the human's, and both stay open. What follows is only what
|
||
the checks now say.
|
||
|
||
### Phase 5 · containment — and the check the page asks for is wrong for half its list
|
||
|
||
| repo | disposition | tips | contained in Sylpheed |
|
||
|---|---|---|---|
|
||
| `Syplheed-Reborn` | absorbed | 21 | **21 ✅** |
|
||
| `Sylpheed-Godot` | absorbed | 2 | **2 ✅** |
|
||
|
||
🔴 **Phase 5 names four repos, but the containment check only means something for
|
||
the two that were *absorbed*.** `xenia-rs` and `xex2tractor` were never absorbed —
|
||
their disposition is *harvest, then archive*. Run the check literally against
|
||
`xenia-rs` and it reports **42 of 42 refs "NOT PRESENT in Sylpheed"**, every line
|
||
true and every line unactionable. That is the failure this page already names once,
|
||
about Phase 0's first gate: *a check that goes red for something unactionable*.
|
||
|
||
**For the two harvest repos the invariant is different** — the harvested artefacts
|
||
are in Sylpheed, and the repository is *archived, not deleted*:
|
||
|
||
| artefact | on `main` | on its PR branch |
|
||
|---|---|---|
|
||
| `docs/reference/xex2-format.md` | ✗ | ✅ #30 |
|
||
| `docs/reference/xbox360-exports.json` | ✗ | ✅ #30 |
|
||
| `LICENSE` | ✗ | ✅ #30 |
|
||
| `crates/sylpheed-xexdb` (32 files) | ✗ | ✅ #32 |
|
||
| `tools/zq.py` | ✅ | ✅ |
|
||
|
||
⚠️ **So the harvest is real but is not on `main` yet.** Archiving is still safe —
|
||
the branches are pushed, and archiving is reversible — but **Phase 5 should follow
|
||
the harvest PRs, not precede them**, or `main` alone does not carry what was
|
||
harvested.
|
||
|
||
### Phase 5 · nothing lives outside git — re-verified, still true
|
||
|
||
| | issues | PRs | releases | wiki |
|
||
|---|---|---|---|---|
|
||
| the four retiring repos | **0** | **0** | **0** | none |
|
||
| `Sylpheed` | 36 | 19 | 0 | none |
|
||
|
||
📌 `has_wiki: true` is the repository *feature flag*, not content —
|
||
`/wiki/pages` returns **404 on all six**, i.e. no wiki was ever initialised. Read
|
||
the flag as content and you would report six wikis that do not exist.
|
||
|
||
📌 All six repos are still **`archived: false`, `private: false`**. The retention
|
||
question this page leaves open is therefore unchanged and now has a number beside
|
||
it: `docs/re/captures/` is ~87 MB of game screenshots in a **public** repository.
|
||
|
||
### Phase 7 · most of the drop list is not on this machine
|
||
|
||
The list was measured on the agent box. Here:
|
||
|
||
| item | page | this machine |
|
||
|---|---|---|
|
||
| `agent-backups/` | 687 MB | **absent** |
|
||
| `xenia-rs/audit-runs/` | 57 MB | **724 KB** |
|
||
| local `pi/clippy`, `pi/clippy-clean`, `pi/reauth3` | 3 branches | **0 — not on this box** |
|
||
| `texcompare/`, `ship_render/` | 14 MB + 64 KB | **both absent** |
|
||
| root `canary_*.log` / `.stdout` / `.stderr` | ~15 MB | 1 file |
|
||
| `stock-oracle/` | 34 MB | 34 MB ✅ |
|
||
| `Sylpheed/target/` | 32 GB | **34 GB** ✅ |
|
||
|
||
✅ **Phase 7's one open question is answered.** It asks to *"confirm no `docs/re/`
|
||
page cites"* `texcompare/` or `ship_render/` before dropping them. Four citations
|
||
of `ship_render` exist — and **all four name the example *program*, not the output
|
||
directory**: `cargo run --example ship_render` in `xbg7-mesh.md`, two in
|
||
`BACKLOG.md`, and the tracked `examples/ship_render.rs` itself. `texcompare` is
|
||
cited nowhere at all. The directories are droppable; the example is tracked code
|
||
and stays. (Same directory-vs-file distinction that made Phase 4's "10 dangling
|
||
citations" turn out to be one.)
|
||
|
||
⚠️ **Disk is tighter than this page records: 90 % used, 96 GB free** (the page says
|
||
83 %). `Sylpheed/target/` is 34 GB of that and is the only large item here — but it
|
||
is a deletion on the human's disk, and rebuilding it is hours, so it stays until
|
||
asked for.
|
||
|
||
## Phase 5 — Verify the invariant, then archive ✅ **DONE 2026-09-16**
|
||
|
||
For each of `Syplheed-Reborn`, `Sylpheed-Godot`, `xenia-rs`, `xex2tractor`:
|
||
re-run the containment check **at that moment** — not from this page's ledger,
|
||
which will be days old — then archive read-only on Gitea, and only then delete
|
||
the local clone.
|
||
|
||
**Archive before delete, and leave them archived.** The check proves the commits
|
||
are reachable. It does not prove nobody will want the repository boundary back.
|
||
|
||
### ⚠️ The fork: history rewrite, or not
|
||
|
||
Dropping files in Phase 4 **reclaims no space**. The 118 MB is in history — 512
|
||
distinct blobs, 120.5 MB — and `git rm` leaves every byte in `.git` (590 MB
|
||
local, 598 MB on the server). Only a rewrite reclaims it, and it has a hard
|
||
blocker:
|
||
|
||
```toml
|
||
# crates/sylpheed-export/Cargo.toml:84
|
||
sylpheed-formats = { git = "…/Sylpheed.git", tag = "formats-pin-2026-09-01" }
|
||
```
|
||
|
||
**The build depends on this repo by tag**, pinned in `Cargo.lock` to `#1cd5b8b1`
|
||
— re-checked 2026-09-14, and the pin is exactly right: `formats-pin-2026-09-01`
|
||
→ `1cd5b8b1`, and the in-container build fetches it over the network for real.
|
||
A rewrite invalidates every `formats-pin-*` tag, every commit SHA, PR #23's
|
||
merge, both agents' clones, and needs a force-push through branch protection.
|
||
|
||
📌 **The tag count is 7, not 8** — server and local agree, and all 7 tags in
|
||
`Sylpheed` are `formats-pin-*`. The eighth, `formats-pin-2026-08-29` (no letter
|
||
suffix), lives in **`Syplheed-Reborn`**, which a Sylpheed rewrite would not
|
||
touch and which Phase 5 archives read-only anyway. It does not change the
|
||
decision; it is one fewer thing to re-cut.
|
||
|
||
* **Don't rewrite** (recommended): 118 MB — and the disk argument has moved.
|
||
The host is now at **90 %, 96 GB free** (this page said 83 % / 154 GB), and
|
||
`Sylpheed/target` alone is **34 GB**. Reclaiming 118 MB by rewriting history,
|
||
while 34 GB of rebuildable build output sits beside it, is still the wrong
|
||
lever.
|
||
* **Do rewrite:** then it happens **here, in Phase 5** — while tags are being
|
||
re-cut and agents re-cloned anyway, so the disruption is paid once instead of
|
||
twice. Re-point the `formats-pin` dependency **first**.
|
||
|
||
This is a decision, not a default. Whichever is chosen, record it here.
|
||
|
||
## Phase 6 — Adopt the orphan assets ✅ **DONE (PR #34)**
|
||
|
||
Tracked by nobody today, and tools or references by the two-repo rule:
|
||
|
||
| asset | → |
|
||
|---|---|
|
||
| `ppc-manual/` — 397 files, **including `generator/generate_manual.py`** | Sylpheed |
|
||
| `docs/ppc_instructions.{json,md}`, `docs/xbox360_exports.*`, `docs/xex2_format.md` | Sylpheed `docs/` |
|
||
| `docs/CROSS_BUILD_SETUP.md` (58 KB) | **Canary** — it builds Canary |
|
||
| `run-canary.sh` | Sylpheed `tools/` |
|
||
| `ai-agent-*.md` ×4 (56 KB) | Sylpheed `docs/`, or discard — human's call |
|
||
| `scratch/xbg7/extract.py` — clean-room XBG7 scanner | superseded by `sylpheed_formats::mesh`; keep as history or drop |
|
||
| `Sylpheed-try/…/examples/_voice_span.rs` — 46-line untracked probe | keep or drop |
|
||
|
||
## Phase 7 — Intended drops ✅ **DONE 2026-09-16**
|
||
|
||
Stated so that nothing is dropped by omission.
|
||
|
||
| drop | size | why it is safe |
|
||
|---|---|---|
|
||
| `agent-backups/` | 687 MB | all 23 refs verified present in Sylpheed |
|
||
| `stock-oracle/` | 34 MB | two Canary **binaries**, rebuildable from source |
|
||
| root `canary_*.log` / `.stdout` / `.stderr` | ~15 MB | run output |
|
||
| `vkd3d-proton.cache` | 15 KB | runtime cache |
|
||
| xenia-rs `audit-runs/` | 57 MB, 41 files | already `.gitignore`d; `iterate-4A` deleted 40 of 41 |
|
||
| local `pi/clippy`, `pi/clippy-clean`, `pi/reauth3` | — | all 4 patches on `main`, by patch-id |
|
||
| `Sylpheed/target/` | **32 GB** | build output; the host is at 83 % |
|
||
| `texcompare/` (14 MB), `ship_render/` (64 KB) | — | tool output — **confirm no `docs/re/` page cites them first** |
|
||
| **the xenia-rs emulator** | ~54k LOC | the human's decision: superseded by Canary |
|
||
|
||
---
|
||
|
||
# STATUS 2026-09-13 — paused here, for the other machine to finish (superseded: see ✅ Closed 2026-09-16 at the top)
|
||
|
||
**Six of eight phases are done and pushed.** Everything below is committed;
|
||
nothing is left in a working tree. Five pull requests are open and **none has
|
||
been merged** — merging is the human's, and #32 contains #30 and #31.
|
||
|
||
| phase | state | where |
|
||
|---|---|---|
|
||
| **0** secure single-copy work | ✅ done | Canary `8e63a9542`, `sylpheed-re` + `audit-handle-lifecycle-probes` pushed |
|
||
| **1** make the archive honest | ✅ done | xenia-rs `iterate-4B/4C/4D/4E` pushed (32/52/56/59 commits) |
|
||
| **0b** the dirt the gate could not see | ✅ done 2026-09-14 | xenia-rs `harvest/import-thunk-naming` `b4f19f1`, ported in `feat/xexdb-import-naming` |
|
||
| **2** harvest xex2tractor | ✅ done | **PR #30** `harvest/xex2tractor-assets` |
|
||
| **3** lift the DB tool | ✅ done | **PR #32** `feat/xexdb-tool` |
|
||
| **4** captures + the missing check | ✅ done | **PR #33** `fix/capture-citations` |
|
||
| **6** adopt the orphan assets | ✅ done | **PR #34** `chore/adopt-orphan-assets`, + Canary `99cc72662` |
|
||
| — containers and agents | ✅ done | **PR #31** `docs/containers-setup` |
|
||
| **5** archive the four repos | ✅ done 2026-09-16 | archived by the human |
|
||
| **7** intended drops | ✅ done 2026-09-16 | |
|
||
|
||
## 🔴 Five claims on this page were wrong. Read these before trusting the rest.
|
||
|
||
1. **"10 dangling citations" was ONE.** Eight were *directory* references, which
|
||
resolve and are simply absent from `git ls-tree`; one was a path at the end of
|
||
a sentence with the full stop pulled into the match. The figure is corrected
|
||
in Phase 4 below and in PR #33.
|
||
2. **`extract -r` is not how the `.pe` was made**, and nothing depends on it —
|
||
Phase 2's gate, run.
|
||
3. **The Phase 0 gate compared branches by SHA** and called seven safe branches
|
||
unpushed. It tests reachability now.
|
||
4. **There is no DXC submodule drift.** The ` m` is *nested* submodule dirt.
|
||
5. **Phase 0 did not secure all the single-copy work**, and its gate could not
|
||
have: it walks `refs/heads`, and 468 uncommitted lines in `xenia-rs` have no
|
||
ref. Secured 2026-09-14 — see the section above Phase 5.
|
||
|
||
## What the other machine should do next
|
||
|
||
### Before anything: merge or decide on the open PRs
|
||
|
||
```
|
||
#30 harvest/xex2tractor-assets reference files + LICENSE
|
||
#31 docs/containers-setup container and agent setup
|
||
#32 feat/xexdb-tool the DuckDB tool ⚠️ CONTAINS #30 and #31
|
||
#33 fix/capture-citations the docs/re check + prune
|
||
#34 chore/adopt-orphan-assets PPC manual + canary launcher
|
||
```
|
||
|
||
⚠️ **#32 is stacked.** Merge #30 and #31 first, or merge #32 and close them —
|
||
its `build.rs` needs #30's export table, so they cannot be independent.
|
||
|
||
✅ **The workspace `cargo test` count for #32 — reported 2026-09-15** (also posted
|
||
on #32). `sylph-ci:local`, rustc 1.98.1 byte-identical to the runner, head `6048829`:
|
||
|
||
| | disc | suites | passed | failed | ignored |
|
||
|---|---|---|---|---|---|
|
||
| `docker/ci/run cargo test --workspace --no-fail-fast` | PRESENT | 45 | **367** | **1** | 14 |
|
||
| + #35, disc not mounted (as the runner) | ABSENT | 45 | 377 | 0 | 14 |
|
||
|
||
The +9 is exactly #35's nine `imports.rs` unit tests. 🔴 **The one failure is `main`'s,
|
||
not #32's**: `ui_prm_primitives_disc` has failed since #23 (bisected; see #32's
|
||
description). It took three failures to get a number, and each one is now a rule:
|
||
|
||
```bash
|
||
SYLPH_CI_MEM_GB=14 docker/ci/run cargo test --workspace --no-fail-fast
|
||
```
|
||
|
||
* **`--no-fail-fast`** — without it the run stops at the first failing binary. An
|
||
earlier attempt reported "25 suites / 201 passed", which was half the workspace.
|
||
* **`SYLPH_CI_MEM_GB`** sized to the host — the default 7 is half of the *agent box*.
|
||
On this 31 GB machine, with the disc mounted, `slb_leading_segment_disc` is
|
||
SIGKILLed: six threads each load a voice bank. Serial passes 10/10 under the
|
||
same 7 GB, so it is concurrency, not one allocation.
|
||
* **~36 GB of disk** for a cold `sylph-ci-target` volume. It took this host from
|
||
90 % to 95 %; it has been deleted since. And a desktop package update that
|
||
upgrades `containerd.io` restarts `dockerd`, killing an in-flight run *and every
|
||
other container on the host* — which is what ended the second attempt.
|
||
|
||
### Phase 5 — archive, and the history fork
|
||
|
||
**Do not trust this page's ledger when you get there.** Re-run the containment
|
||
check *at that moment* — days will have passed and branches may have moved:
|
||
|
||
```bash
|
||
for repo in Syplheed-Reborn Sylpheed-Godot xenia-rs xex2tractor; do
|
||
# every ref of $repo must be an ancestor of a Sylpheed ref
|
||
done
|
||
```
|
||
|
||
Then **archive read-only on Gitea**. 🔴 **Do not delete any repository.** Archiving
|
||
is reversible; deletion is not, and it is the human's call, separately, later.
|
||
Nothing outside git needs migrating — issues, PRs, releases and wikis are **zero**
|
||
in all four (verified).
|
||
|
||
The **history-rewrite fork** is still open and still recommended AGAINST: the
|
||
118 MB is in history, and `crates/sylpheed-export/Cargo.toml` depends on this
|
||
repo **by tag** (`formats-pin-2026-09-01`, pinned in `Cargo.lock`). A rewrite
|
||
invalidates all 8 tags, every SHA, PR #23's merge and both agents' clones.
|
||
|
||
### Phase 7 — the drops
|
||
|
||
All verified, none done — they are deletions on the human's disk:
|
||
|
||
```
|
||
agent-backups/ 687 MB 23 refs verified present in Sylpheed
|
||
Sylpheed/target/ 32 GB build output; the host is at 83 %
|
||
stock-oracle/ 34 MB two Canary binaries, rebuildable
|
||
root canary_*.log 15 MB run output
|
||
xenia-rs audit-runs/ 57 MB already .gitignore'd; iterate-4A deleted 40 of 41
|
||
pi/clippy, pi/clippy-clean, pi/reauth3 all 4 patches on main, by patch-id
|
||
texcompare/ (14 MB), ship_render/ confirm no docs/re/ page cites them first
|
||
```
|
||
|
||
### Still the human's, untouched
|
||
|
||
`ai-agent-*.md` ×4 (56 KB), `scratch/xbg7/extract.py`, `_voice_span.rs`, and the
|
||
**retention question**: all six repos are `private=False`, and `docs/re/captures/`
|
||
is ~87 MB of game screenshots in a public repository.
|
||
|
||
## What each finished phase actually produced
|
||
|
||
* **Phase 2** — `docs/reference/{xex2-format.md, xbox360-exports.*, ppc-instructions.*}`
|
||
and an MIT `LICENSE`; this repo had none. The first two are byte-identical to
|
||
xex2tractor's, verified with `cmp`.
|
||
* **Phase 3** — `sylpheed-xex` + `sylpheed-ppc` + `sylpheed-xexdb`, ≈17,200 lines
|
||
of ~71,000, coupled to the emulator by **three symbols**. The gate: a
|
||
regenerated database whose schema is a superset, with `instructions`,
|
||
`pdata_entries`, `imports`, `sections` and `eh_*` identical to the row. The two
|
||
−1.8 M deltas are the `max_indirect_candidates` ceiling, recorded by the tool
|
||
as **6,556 truncated sites standing for 1,801,075 candidates** — checked, not
|
||
assumed: the xref delta is entirely `ind_call`. `build.rs` no longer reads a
|
||
sibling checkout, and a missing export table now **fails** instead of silently
|
||
producing a database with no import names.
|
||
* **Phase 4** — `tools/re/check-capture-citations`, 212 captures all cited, 46
|
||
orphans dropped. It caught its own damage twice: two "orphans" are opened by
|
||
*filename* from `screen_match.py`, and the first prune emptied two directories
|
||
that pages cite *as directories*.
|
||
* **Phase 6** — `tools/ppc-manual/` (393 files) and a rewritten
|
||
`tools/run-canary.sh`; the old one pointed at a **symlink** Wine cannot resolve
|
||
and hardcoded one machine's paths. The full `CROSS_BUILD_SETUP.md` went to
|
||
Canary, where the tracked copy was a 6.8 KB stub of a 59 KB guide.
|
||
|
||
## The method, if you only read one thing
|
||
|
||
Every claim on this page that was *reasoned* turned out wrong, and every one that
|
||
was *run* held. The gates are the point: Phase 2's gate refuted its own premise,
|
||
Phase 3's proved the two alarming row deltas were an improvement, and Phase 4's
|
||
check caught a deletion I had just made. **Run them again rather than trusting
|
||
the results recorded here.**
|
||
|
||
## Still open, for the human
|
||
|
||
* **Retention.** The `.iso` (7.6 GB), `.pe`, `.xex.json` and `sylph_extract/`
|
||
(6.2 GB) stay on disk and out of git. But `docs/re/captures/` is 118 MB of
|
||
game screenshots **inside a public repository**, about to be joined by a
|
||
938 KB export database, and **all six repos are `private=False`**. Worth
|
||
choosing rather than inheriting.
|
||
* **The history fork** in Phase 5.
|
||
* **`ai-agent-*.md`, `scratch/`, `_voice_span.rs`** — keep or drop.
|