Files
Sylpheed/docs/agents/CONSOLIDATION.md
Claude 46ee0d7f78
Some checks failed
CI / Native — linux (pull_request) Failing after 5m14s
CI / WASM — Web (pull_request) Successful in 36m12s
CI / Formatting (pull_request) Successful in 1m40s
chore(agents): restore evidence sharing after the asset purge
Issue #49 removed game assets from git. It also, silently, removed the only
transport agents had for showing each other evidence: each agent works in its
OWN clone, so "commit the screenshot, the other one pulls it" was the mechanism,
and PROTOCOL.md's file table said so in as many words -- "evidence cited by a
finding -> git". That row sat directly above "🔴 Never commit game content",
which is how 76 MB accumulated: the two contradicted each other and the table
won, because it was the one that told you what to do.

WHAT REPLACES IT

  * one host directory, `Sylpheed/docs/re/captures/`, bind-mounted read-write
    into BOTH agents at /work/docs/re/captures. All three -- host, decoder,
    port -- see the same files live, every citation resolves, and nothing can
    reach git history. Read-write on purpose: showing each other a screenshot
    is the point.
  * PROTOCOL.md's table rewritten. Cited evidence -> present but never
    committed; derived measurements (csv/tsv/txt/log/json) -> still git, they
    are our numbers not game content; evidence that must cross MACHINES ->
    attached to the issue or PR, because a bare clone has no captures.

Verified, not assumed: container A wrote a .png there, a SEPARATE container B
read it back, the host saw it, `git status` reported 0 changes, and
`git check-ignore` named the rule.

THE CHECKER WAS RED ON EVERY CLEAN CHECKOUT

A fresh clone/worktree/CI has no captures, so it called all 134 citations
dangling and exited 1. A gate that is red before anyone changes anything is one
people learn to ignore -- the exact failure this file already carries a comment
about. It now distinguishes "no captures here" (expected, explains itself,
exit 0) from "these are missing" (real, exit 1, unchanged when assets ARE
present). Both paths tested.

ALSO
  * `sylph-decoder` no longer mounts `xenia-rs` -- retired repo, gone from disk,
    the mount pointed at nothing.
  * CONSOLIDATION.md closed: it still described captures as committed and the
    history fork as undecided. Both are settled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 13:12:26 +02:00

698 lines
36 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ✅ CLOSED 2026-09-20 — every phase done, and the two human decisions taken
Nothing on this page is outstanding. It is kept as the record of how the two-repo
layout was reached; **where it describes the repository's present state it is
now out of date on purpose**, and the notes below say how.
| the page says | what is true now |
|---|---|
| "**Still open, for the human**: the history fork" | **Taken: full purge.** `git filter-repo` stripped 182 assets from every commit; force-pushed 2026-09-19. `main` `2a93be99``e16556dc`, `.git` **217 MB → 114 MB**, **0** assets anywhere in history, verified from an independent fresh clone. Backups + runbook: `~/sylph-repo-backup-2026-09-19/` |
| "`docs/re/captures/` is 118 MB of game screenshots inside a public repository" | **Decided (#49): no game assets in the repos.** Captures live on disk, gitignored, shared between the host and both agents by bind mount; derived measurements (csv/tsv/txt/log/json/npy) stay tracked. PR #59 |
| "all six repos are `private=False`" | Still true, and now deliberate — there is nothing game-derived in them |
| "`ai-agent-*.md`, `scratch/`, `_voice_span.rs` — keep or drop" | `scratch/` and `_voice_span.rs` dropped in the 2026-09-18 cleanup; the `ai-agent-*.md` files are the human's and stay |
| the four archived repos | unchanged: `Sylpheed-Godot`, `xenia-rs`, `xex2tractor` archived read-only, `Syplheed-Reborn` deleted. ⚠️ Their local clones are **gone from this machine** too, and `sylph-decoder` no longer mounts `xenia-rs` |
🔴 **The one thing this page got right and nobody acted on.** It named
`crates/sylpheed-export`'s dependency on **this repository by tag** as the hard
blocker for a rewrite. It was not re-checked before the purge, the locked rev
vanished, and the build broke for every clean checkout while still working on the
machine that did the rewrite — its `~/.cargo/git` still held the old object.
Fixed in PR #60 (re-lock) and PR #61 (path dependency, which removes the class).
**Before any future history rewrite, grep for a dependency on the repo's own URL.**
---
# 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:0121: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.