Removes the last reason this repository depends on its own history.
The pin was deliberate and carried its own exit condition, written into the
comment above it: "revert to the path dependency the day the tag is an ancestor
of `main`." Measured -- `formats-pin-2026-09-01` (e2630413) IS an ancestor of
`main` now, so the condition is met, and the cost the comment named goes away
with it: while the pin held, `sylpheed-cli` built from the workspace crate and
the exporter from the tag, so `tools/port/verify-screen` compared two eras
instead of detecting drift. They read one decoder again.
It also removes a failure mode nobody priced in. Depending on this repo by tag
is what made the #49 history rewrite break the build: every commit was replaced,
the locked rev vanished, and clean checkouts could not resolve it while the
rewriting machine kept working off its `~/.cargo/git` cache (PR #60). A path
dependency cannot fail that way. `Cargo.lock` now has ZERO references to
Sylpheed.git.
⚠️ This moves the exporter across a 243-file decoder change (8 467 insertions),
so it was gated on the full suite rather than a compile:
45 suites / 377 passed / 0 failed / 14 ignored, cargo exit 0
45 binaries launched, 45 reported (a SIGKILLed suite prints no result line
and would otherwise vanish from the tally)
corpus report: PRESENT for disc, res3d and iso
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Finishes #16 in the three places its earlier remedies missed.
`tests/`: the last four local `disc_root()` copies now use `tests/common`, and
with them goes the one real hardcoded fallback — `ui_keyframe_record_disc.rs`
fell back to an absolute path on one machine, which made `unset SYLPHEED_DISC`
a no-op there. Control: with the corpus absent that suite now finishes in 0.00s
instead of 57.55s, so it skips rather than finding a disc of its own.
`examples/`: seventeen examples defaulted to `/disc`, the mount point inside the
CI container. Redundant there — `docker/ci/run` sets `SYLPHEED_DISC=/disc` — and
wrong everywhere else, where a missing corpus turned into a file-not-found
against a path that has never existed on the host. They now name the variable to
set, like the other hundred examples already did.
`docker/ci/run`: mount `$SYLPHEED_RES3D` and `$SYLPHEED_ISO` alongside the disc.
Only the disc was mounted, so an in-container run sat out the res3d and iso
suites while looking like a full one — the defect this issue is about, in the
runner itself.
Measured in the container on this desktop with all three corpora present:
45 suites / 377 passed / 0 failed / 14 ignored, and `sylpheed-corpus-report.txt`
now reports PRESENT for all three rather than for the disc alone.
Refs #16.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`cargo fmt --all -- --check` has failed on every run in this repository's
history, identically on `main` and on every branch. This is #12.
Mechanical: `cargo fmt --all`, nothing else. 154 files, all `.rs`, no other
extension touched. `cargo check --workspace` exits 0 afterwards, so nothing
changed semantically.
ON THE ORDERING, WHICH WAS THE REAL QUESTION.
HANDOFF-2026-09-06 section 7 warns this is the expensive fix: a whole-tree
reformat before #7 and #8 return "would put a conflict in every file of 861
commits and make the reviews those items exist to enable unreadable".
That is measurably too pessimistic, and it had been reasoned rather than
tested. Measured here by three-way merging a rustfmt'd `main` against both
unmerged branches, file by file:
file/branch pairs tested 32
merges CLEAN 28
merges CONFLICTING 4 (8 conflict hunks total)
sylpheed-cli/src/main.rs 1 hunk
sylpheed-export/src/check.rs 1
sylpheed-export/src/screen.rs 4
sylpheed-export/src/video.rs 2
All four are against `auto/frame-blend-draw-path` only;
`auto/port-p6-audio` does not conflict anywhere. The earlier framing --
154 dirty files, 133 that cannot collide, 21 that can, the collision set
carrying 147 of 774 hunks (19%) -- reproduces exactly. What it did not say
is that most of the 21 still merge cleanly, because rustfmt's edits and the
branches' edits rarely land on the same lines.
So the cost of sweeping now is 4 files and 8 hunks for one branch, against
a check that is otherwise red forever. Deliberately NOT folded into the
WASM PR: 154 reformatted files would make that one unreviewable.
Closes#12
`cargo clippy --workspace -- -D warnings` now exits 0. `cargo test
--workspace` still reports 207 passed, 0 failed, 14 ignored across 30
suites — identical to runs 203 and 204, so none of this changed behaviour.
The workspace total was 73, not the 48 run 204 reported. `-D warnings`
turns a lint into a hard compile error, so `sylpheed-formats` failing
stopped its dependents from ever being built: `sylpheed-viewer` (14) and
`sylpheed-cli` (11) had never been linted by anyone. Clearing formats in
bc79817 is what made them visible.
formats 43 -> 0 (bc79817)
viewer 14 -> 0
cli 11 -> 0
export 5 -> 0
Collision surface, measured rather than assumed. Every viewer file
carrying a lint is byte-identical on both `auto/frame-blend-draw-path`
(495 commits) and `auto/port-p6-audio` (366). All eleven cli sites fall
outside every hunk either branch touches. 68 of the 73 sites could not
collide with anything.
The five that can are all in `sylpheed-export`, and three of those are
real:
main.rs:278 `&out` -> `out`, inside frame-blend's hunk -278,12
main.rs:318 `&out` -> `out`, inside port-p6-audio's hunk -303,44
audio.rs:113 an added `#[allow]` in a file frame-blend DELETES
Each is one line. Resolving the first two means taking the branch's
version and re-applying a borrow removal; the third resolves to the
deletion. Flagged here so neither branch owner meets them cold.
Judgement calls, all stated at the site rather than suppressed globally:
* Three `too_many_arguments` in the viewer are false positives.
`draw_viewer_ui`, `poll_loader_channel` and `apply_pak` are Bevy
systems — every parameter is a `Res`/`ResMut`/`EventWriter` the
scheduler injects, so the count is the framework's dependency list and
cannot be reduced without a `SystemParam` struct.
* `cmd_screen_render` (cli, 8/7) is a plain function, so that one is real
if mild; its arguments are the subcommand's flags.
* Two `dead_code` fields in export are serde schema fields. They model
what the on-disc JSON accepts; deleting them would quietly change that.
* `iso_loader.rs` gains a `FrameRx` alias for the ffmpeg frame channel,
which is what "very complex type" was asking for.
A site-local `#[allow]` with a reason is a decision recorded where it
applies: one lint, one function, and any new violation elsewhere still
fails the build. That is not the shape PROTOCOL.md forbids.
Closes#13
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01McNbzUeq1KRBWs4G6X2YVj
Takes the port branch up to 77320d5e -- the state the human play-tested on
2026-09-02 -- for SOURCE paths only. Not a branch merge: `auto/port-p6-audio`
is 366 commits and 938 files, and most of that must not land.
WHAT COMES IN (76 files, all human-confirmed working):
* the logo splash animation. 08ed3dd1 found it: `pose_at` ASSIGNED the settle
instant instead of clamping to it, so the splash never animated at all --
and the same bug manufactured a passing harness result, because the harness
photographed t past the settle. Confirmed by play-test: "cannot notice any
obvious difference from the actual game."
* gamepad input -- (A)/(B) bound additively (`ui_accept` ships with NO joypad
binding), stick latched with hysteresis at the game's own 61% digitise
threshold. This is what made (A), video-skip and Extras work at all.
* menu navigation and flow, menu audio, the exporter, the authored
declarations, and 23 verification tools under tools/port/.
WHAT IS DELIBERATELY LEFT ON THE BRANCH:
* everything after c0ae460a -- the F5/F6 title-timing investigation, whose own
tip commit calls itself a "hand-off for one-minute human checks". Unchecked
by definition; it goes through the new review gate like anything else.
* the OPTIONS menu work of 2026-09-03. Real, probably good, NOT play-tested.
* the F1 repeat mechanism, which its own commit calls "deliberately inert".
WHAT MUST NOT LAND, AND WHY THE .gitignore CHANGED:
545 MB of extracted game content was committed on that branch -- 850 sprite,
audio and transcoded video files under `export-probe/` and `export-probe2/`,
plus 246 MB of loose .wav and .tsv at the repo root. This repository's own
rule, in this file, is "never game content".
The rule was not missing. It was written, and it was tightened on that very
branch, with a careful comment explaining why BOTH `export/` and `data/base/`
had to be listed -- while the exporter was writing to a third name that
nobody had thought to list. Enumerating names is the thing that failed. So
the ignore rules now describe the SHAPE: any top-level `export*/`, game media
by extension, and loose capture output at the root. Verified both ways -- it
catches all four offenders and ignores nothing currently tracked.
Verified: `cargo check --workspace` clean; all nine GDScript files parse in
project context, with a positive control (an injected syntax error is detected,
3 lines) so the clean result means something. `tools/port/check-all` was NOT
run -- it needs the container, the export tree and a display.
Merges the Godot port into the reverse-engineering repository, preserving both
histories -- 1019 commits of corpus plus the port's 31, brought in by subtree
merge and then moved into place so git can follow each file across the rename.
The reason is not tidiness. The two-repo split forced the exporter to depend on
the decoders by pinned revision, and that created a whole class of failure that
now disappears: a sha reachable only from a topic branch, orphaned by a
squash-merge, breaking a fresh checkout silently at build time. It also forced a
live read-only mount of one agent's working tree into another's container, which
is why a contract file could move mid-iteration. With a path dependency, a
decoder change and the exporter change it requires land in the same commit or
not at all.
Canary stays separate: it is a fork tracking upstream.
New structure for the long term:
docs/game/ how the game is NAVIGATED -- menus, modals, prompts, alerts,
and in-game flight. Written so nobody rediscovers it. Mostly
open questions on purpose; the in-game tutorials are the
resource for the flight half.
docs/port/MODDING.md
modding as a constraint on the exporter TODAY, not a later
feature: one logical asset in one file (the disc splits nearly
everything, and resolving that is the exporter's job), names a
person recognises, PNG/OGG/OGV/JSON only, base-and-overrides so
re-exporting is always safe, provenance in every file.
data/base + data/mods
generated tree and drop-in overrides, both gitignored
exchange/ transient inter-agent files, deliberately outside history
docs/agents/ the team protocol
Both the README and the navigation doc lead with the correction that cost the
most: the oracle is the real game under Xenia Canary. Reborn's renderer is a
hypothesis under test, it has been wrong, and treating it as ground truth
propagated into three documents and both agents before a human caught it.
Scripted modding stays possible without being built: no screen name is hardcoded
in GDScript and there is no native code in port/, which is what Godot Mod Loader
needs to be able to substitute behaviour later.