Compare commits

..

2 Commits

Author SHA1 Message Date
9414830ed7 Merge pull request 'fix(canary): point the launchers at a binary that exists and is instrumented' (#63) from fix/canary-native-binary-path into main
Some checks failed
CI / WASM — Web (push) Has been cancelled
CI / Formatting (push) Has been cancelled
CI / Native — linux (push) Has been cancelled
Reviewed-on: #63
2026-09-21 16:03:06 +00:00
884a2c3944 fix(canary): point the launchers at a binary that exists and is instrumented
Some checks failed
CI / Native — linux (pull_request) Failing after 1h3m36s
CI / WASM — Web (pull_request) Successful in 24m29s
CI / Formatting (pull_request) Successful in 27s
`/sylph-canary` was dead at both ends, measured rather than assumed:

  * `run-canary-safe.sh` runs the Wine build and defaults to
    `xenia_canary_i2d.exe`, which DOES NOT EXIST. The `xenia_canary.exe` that
    does (2026-06-19) has ZERO strings for `audit_61_branch_probe_pcs` and zero
    for `RE-INPUT`/`RE-DRAW` -- it predates all our instrumentation, and
    refreshing it needs the clang-cl + xwin toolchain on `cross-build-wine`.
  * both native launchers defaulted to
    `xenia-canary-native/build/bin/Linux/Release/xenia_canary`. That directory
    does not exist on this workspace -- the checkout is `xenia-canary/` -- and
    Release is not even a target in that build tree (`ninja -n` lists 0 steps),
    so its binary is frozen at 2026-08-28, before the probe.

Native launchers now default to
`xenia-canary/build/bin/Linux/Checked/xenia_canary`: the config this tree can
actually build, rebuilt today with the audit_61 probe (Canary PR #2), verified
by `strings` -- audit_61 8 hits, RE-INPUT/RE-DRAW 4. `Checked` is optimised WITH
assertions, which is what a behavioural reference should be: a bad state stops
loudly rather than being sampled.

The Wine launcher now ABORTS with the reason and points at the native one,
instead of failing somewhere downstream on a missing file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 21:00:13 +02:00
9 changed files with 61 additions and 102 deletions

View File

@@ -10,22 +10,11 @@
# AVAILABLE MEMORY as well as core count — a full-parallel build of this tree # AVAILABLE MEMORY as well as core count — a full-parallel build of this tree
# has OOM-killed the host outright. # has OOM-killed the host outright.
# #
# `Checked` is the default, and should stay it: it is what the host builds and # build-canary [Release|Debug] [extra cmake --build args]
# what `run-canary` picks first, so the two agree by construction. Building
# `Release` here instead leaves a second binary that `run-canary` will not use
# -- hours of CPU for something nothing runs. `Checked` is optimised, with
# assertions left in.
#
# build-canary [Checked|Release|Debug] [extra cmake --build args]
set -euo pipefail set -euo pipefail
CONFIG="${1:-Checked}"; shift || true CONFIG="${1:-Release}"; shift || true
# $XENIA_SRC FIRST. In this container the Canary source is bind-mounted at SRC="${PROJECT_DIR:-/work}/xenia-canary"
# `/canary`, and `$PROJECT_DIR/xenia-canary` does not exist -- so the old default
# made this script unusable here, and the documented workaround was to symlink
# `/canary` into the repository and remember to delete it again (CONTAINER-NOTES
# §"The toolchain is real"). The launcher already exports the right path; read it.
SRC="${XENIA_SRC:-${PROJECT_DIR:-/work}/xenia-canary}"
BUILD="${XENIA_BUILD_DIR:-/sylph-home/re/canary-build}" BUILD="${XENIA_BUILD_DIR:-/sylph-home/re/canary-build}"
JOBS="${SYLPH_JOBS:-2}" JOBS="${SYLPH_JOBS:-2}"

View File

@@ -48,25 +48,14 @@ fi
PROJECT_DIR="${PROJECT_DIR:-/work}" PROJECT_DIR="${PROJECT_DIR:-/work}"
# ── Binary ─────────────────────────────────────────────────────────────────── # ── Binary ───────────────────────────────────────────────────────────────────
# `Checked` FIRST. All three configurations can be built, but `Checked` is the
# one this project actually builds, so it is the one carrying our
# instrumentation; the `Release/` and `Debug/` binaries beside it are months-old
# leftovers that still run and still boot the game, which is exactly what makes
# them dangerous -- a probe run against one reports zero hits and reads as a
# finding about the game. Measured on this box 2026-09-21: `Checked` has
# `audit_61_branch_probe_pcs`, `Release` (Aug 28) and `Debug` (Jul 19) do not.
pick_bin() { pick_bin() {
[ -n "${XENIA_BIN:-}" ] && { echo "$XENIA_BIN"; return; } [ -n "${XENIA_BIN:-}" ] && { echo "$XENIA_BIN"; return; }
# Configuration is the OUTER loop, location the inner one: a `Checked` build for c in \
# anywhere beats a `Release` build anywhere. The other order picks a stale "${XENIA_BUILD_DIR:-/sylph-home/re/canary-build}/bin/Linux/Release/xenia_canary" \
# container `Release` over a fresh instrumented repo `Checked`, which is the "${XENIA_BUILD_DIR:-/sylph-home/re/canary-build}/bin/Linux/Debug/xenia_canary" \
# exact mistake this is here to stop. "$PROJECT_DIR/xenia-canary/build/bin/Linux/Release/xenia_canary" \
for cfg in Checked Release Debug; do "$PROJECT_DIR/xenia-canary/build/bin/Linux/Debug/xenia_canary"; do
for c in \ [ -x "$c" ] && { echo "$c"; return; }
"${XENIA_BUILD_DIR:-/sylph-home/re/canary-build}/bin/Linux/$cfg/xenia_canary" \
"$PROJECT_DIR/xenia-canary/build/bin/Linux/$cfg/xenia_canary"; do
[ -x "$c" ] && { echo "$c"; return; }
done
done done
} }
BIN="$(pick_bin)" BIN="$(pick_bin)"
@@ -75,13 +64,6 @@ if [ -z "${BIN:-}" ]; then
exit 1 exit 1
fi fi
# Say what is in the binary before the run, not after reading an empty log.
# A missing probe is a build that predates it, never a quiet game.
for sym in audit_61_branch_probe_pcs RE-DRAW; do
grep -aqm1 -- "$sym" "$BIN" \
|| echo "run-canary: ⚠ $BIN has NO '$sym' -- it predates that instrumentation. Rebuild with: build-canary" >&2
done
# ── ISO ────────────────────────────────────────────────────────────────────── # ── ISO ──────────────────────────────────────────────────────────────────────
ISO="${SYLPH_ISO:-}" ISO="${SYLPH_ISO:-}"
if [ -z "$ISO" ]; then if [ -z "$ISO" ]; then

View File

@@ -123,16 +123,7 @@ docker_args() {
# emulator and scrape /dev/shm to get at it. An earlier belief that this # emulator and scrape /dev/shm to get at it. An earlier belief that this
# file was STALE was tested and refuted -- it is current. # file was STALE was tested and refuted -- it is current.
-v "${SYLPH_PE:-$PROJECT/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).pe}:/image/sylpheed.pe:ro" -v "${SYLPH_PE:-$PROJECT/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).pe}:/image/sylpheed.pe:ro"
# 🔴 THE DISASSEMBLY DATABASE. This used to be read from the xenia-rs clone -e "SYLPHEED_DB=/xenia-rs/sylpheed.db"
# (`SYLPHEED_DB=/xenia-rs/sylpheed.db`). That repo is retired, its directory
# is gone, and the mount that served it was removed -- so the variable named
# a path that did not exist and `zq.py` had nothing to open. The database now
# lives beside the repo as a build artefact; mounted at the container's repo
# root, which is where `zq.py` looks when `$SYLPHEED_DB` is unset.
#
# Read-only on purpose: the host owns it, DuckDB takes an exclusive lock to
# write, and two agents plus the human sharing one file would corrupt it.
# Regenerating means writing elsewhere and pointing `$SYLPHEED_DB` at it.
-e "SYLPHEED_PE=/image/sylpheed.pe" -e "SYLPHEED_PE=/image/sylpheed.pe"
-e "SYLPHEED_IMAGE_BASE=0x82000000" -e "SYLPHEED_IMAGE_BASE=0x82000000"
# 🔴 EVIDENCE IS SHARED, NOT COMMITTED (issue #49). Each agent works in its # 🔴 EVIDENCE IS SHARED, NOT COMMITTED (issue #49). Each agent works in its
@@ -285,20 +276,6 @@ docker_args() {
echo "==> NOTE: no ISO at $_iso -- Canary has nothing to boot." >&2 echo "==> NOTE: no ISO at $_iso -- Canary has nothing to boot." >&2
fi fi
# The disassembly database. `zq.py` looks at the repo root when $SYLPHEED_DB
# is unset, so mounting it there needs no environment variable at all -- which
# is what the dead `/xenia-rs/sylpheed.db` variable taught: a path in an env
# var and a path in a mount drift apart silently.
_db="${SYLPH_DB:-$PROJECT/Sylpheed/sylpheed.db}"
if [ -f "$_db" ]; then
_out+=(-v "$_db:/work/sylpheed.db:ro")
else
echo "==> NOTE: no sylpheed.db at $_db -- /sylph-dis and zq.py cannot run." >&2
echo " build one: cargo run --release -p sylpheed-xexdb --bin sylph-xexdb -- \\" >&2
echo " dis \"\$SYLPH_ISO\" --db sylpheed.db --analyze sql --quiet" >&2
echo " then re-apply the RE names with tools/apply_re_symbols.sql" >&2
fi
# ── GPU ── # ── GPU ──
# Three distinct cases, and conflating them is how you end up believing you # Three distinct cases, and conflating them is how you end up believing you
# have hardware Vulkan while actually running llvmpipe: # have hardware Vulkan while actually running llvmpipe:

View File

@@ -12,20 +12,13 @@ already went wrong once.
* **The toolchain is real.** `tools/re-capture/rebuild_canary.sh` exists because * **The toolchain is real.** `tools/re-capture/rebuild_canary.sh` exists because
the old box had no cmake/ninja/clang and only runtime sonames, so it hand- the old box had no cmake/ninja/clang and only runtime sonames, so it hand-
relinked object files. **Do not use it here.** Use `build-canary`. relinked object files. **Do not use it here.** Use `build-canary`.
**FIXED 2026-09-21 — `build-canary` now reads `$XENIA_SRC` first**, which the 🔴 **But `build-canary` does not work in this container as it stands
launcher already sets to `/canary`, so there is no symlink to make and none to
forget. The two paragraphs below are kept because they are how the defect was
found and refuted, but **neither describes the script any more.**
🔴 ~~**`build-canary` does not work in this container as it stands
(2026-08-29).** It builds `${PROJECT_DIR:-/work}/xenia-canary`, which **does (2026-08-29).** It builds `${PROJECT_DIR:-/work}/xenia-canary`, which **does
not exist here** — the Canary source is at **`/canary`** (`$XENIA_SRC`). The not exist here** — the Canary source is at **`/canary`** (`$XENIA_SRC`). The
warm 235 MB tree at `/sylph-home/re/canary-build` is configured with warm 235 MB tree at `/sylph-home/re/canary-build` is configured with
`CMAKE_HOME_DIRECTORY=/work/xenia-canary`, also missing, and its `CMAKE_HOME_DIRECTORY=/work/xenia-canary`, also missing, and its
`build-Release.ninja` carries **no per-file rules** — it wants to re-run CMake `build-Release.ninja` carries **no per-file rules** — it wants to re-run CMake
first, which would fail on the absent source root.~~ The warm tree is gone too: first, which would fail on the absent source root.
every `sylph-*` volume was removed in the 2026-09-18 cleanup, so the next build
configures from scratch against `/canary` and caches the right source root.
**The conclusion drawn from all that is REFUTED (2026-08-31).** The note went **The conclusion drawn from all that is REFUTED (2026-08-31).** The note went
on to say *"any Canary change is a full reconfigure against `/canary` plus a full on to say *"any Canary change is a full reconfigure against `/canary` plus a full
@@ -45,10 +38,9 @@ already went wrong once.
warm 235 MB tree is otherwise intact and the ninja re-run resolves its rules warm 235 MB tree is otherwise intact and the ninja re-run resolves its rules
from the symlink. from the symlink.
⚠️ ~~**Remove the symlink when you are done.**~~ No longer needed — but if you ⚠️ **Remove the symlink when you are done.** `/work` is the repository, and
ever make one by hand, note that `/work` is the repository and `xenia-canary` `xenia-canary` is not in `.gitignore`, so it shows up as untracked and can be
is not in `.gitignore`, so it shows up as untracked and can be swept into a swept into a `git add -A`.
`git add -A`.
* **numpy and Pillow are installed.** `entities2.py`, `flight_probe.py` and the * **numpy and Pillow are installed.** `entities2.py`, `flight_probe.py` and the
image oracles work. Their absence used to look like a logic bug. image oracles work. Their absence used to look like a logic bug.

View File

@@ -56,28 +56,11 @@ rather than left in a document you might not reach.
| path | what | env | | path | what | env |
|---|---|---| |---|---|---|
| `/image/sylpheed.pe` | the decompressed executable image | `SYLPHEED_PE` | | `/image/sylpheed.pe` | the decompressed executable image | `SYLPHEED_PE` |
| `sylpheed.db` at the repo root | the disassembly database, 337 MB | — (`zq.py` finds it; `$SYLPHEED_DB` only to override) | | `/xenia-rs/sylpheed.db` | a disassembly database, 586 MB | `SYLPHEED_DB` |
| `/canary/build/bin/Linux/Checked/xenia_canary` | the built oracle, inside the `/canary` mount | — |
| `/disc` | the extracted disc | `SYLPHEED_DISC` | | `/disc` | the extracted disc | `SYLPHEED_DISC` |
| `/iso/game.iso` | the retail ISO Canary boots | `SYLPH_ISO` | | `/iso/game.iso` | the retail ISO Canary boots | `SYLPH_ISO` |
| `/canary` | the Canary source, read-write | `XENIA_SRC` | | `/canary` | the Canary source, read-write | `XENIA_SRC` |
⚠️ **The database is read-only and is NOT in git** — it is a build artefact, and
`docs/re/captures/` is the same (issue #49). If either is missing the launcher
says so and prints the command that builds it. Query the database with
`python3 tools/zq.py …`, never by reading a disassembly dump.
⚠️ **Take the oracle from `Checked/`, not `Release/`.** `Checked` is what this
tree actually builds (optimised, with assertions); the `Release/` binary beside
it is a stale August build with no `audit_61` probe in it. `strings` on the two
is how that was found, and is how to check any binary before quoting a run from
it.
The `Checked` build carries all three instrumentations — `RE-INPUT`, `RE-DRAW`
and the `audit_61` guest-PC branch probe (fork PR #2 brought the probe onto
`sylpheed-re`; before it, the probe lived only on `phase-a-tracing`). Verified
by `strings` on the binary, not assumed.
**The `.pe` is a flat VA dump**: file offset = `VA - 0x82000000`. Reading **The `.pe` is a flat VA dump**: file offset = `VA - 0x82000000`. Reading
`0x820A1630` is `seek(0xA1630)`. No XEX decrypt, no LZX, **no booted emulator** `0x820A1630` is `seek(0xA1630)`. No XEX decrypt, no LZX, **no booted emulator**
dumping guest memory works but makes the whole static corpus depend on a running dumping guest memory works but makes the whole static corpus depend on a running

View File

@@ -19,13 +19,12 @@ The instrumented branch stops at the Stage 02 briefing under a storm of
behind `upstream/canary_experimental` (`a5a18f5c7`); our branch carries 50 of its behind `upstream/canary_experimental` (`a5a18f5c7`); our branch carries 50 of its
own. own.
* ~~**`version.h` is never generated.**~~ ✅ **FIXED — `CMakeLists.txt` now * **`version.h` is never generated.** The build fails on
generates it at configure time**, calling `xenia-build.py`'s `trace_writer.cc:17: fatal error: 'version.h' file not found`. Upstream's
`generate_version_h()` and falling back to a stub if that fails, so a `xenia-build.py` writes it from git HEAD; the container's `build-canary`
CMake-direct build no longer depends on a stale copy in the build directory. wrapper does not invoke it, and our tree only builds because a stale copy from
It used to fail on `trace_writer.cc:17: fatal error: 'version.h' file not an old `sylpheed-re` build sits in the build directory. Regenerated by hand in
found`, and the fix had to exist before the 2026-09-18 cleanup deleted the exactly the format that script emits.
build volume that was carrying that stale copy.
* **`build-canary` reports success on a failed build.** The harness recorded * **`build-canary` reports success on a failed build.** The harness recorded
"completed (exit code 0)" while ninja had stopped with `1 error generated`. "completed (exit code 0)" while ninja had stopped with `1 error generated`.
Only the missing binary gave it away. Only the missing binary gave it away.

View File

@@ -32,7 +32,18 @@ export ALSA_CONFIG_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/asound-nu
# Paths derive from where this script sits — see the note in run-canary-safe.sh. # Paths derive from where this script sits — see the note in run-canary-safe.sh.
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE="$(cd "$HERE/../.." && pwd)" WORKSPACE="$(cd "$HERE/../.." && pwd)"
BIN_DEFAULT="$WORKSPACE/xenia-canary-native/build/bin/Linux/Release/xenia_canary" # 🔴 THE INSTRUMENTED BINARY IS THE `Checked` ONE, and it is the only config this
# build tree can produce: `ninja -n bin/Linux/Release/xenia_canary` lists ZERO
# steps, so Release is not a target here and its binary still dates from
# 2026-08-28, before the audit_61 probe existed. Pointing at Release therefore
# runs an oracle with no probe and no way to refresh it.
#
# The old default named `xenia-canary-native/`, a directory that does not exist
# on this workspace at all -- the checkout is `xenia-canary/`.
#
# `Checked` is optimised WITH assertions, which is what you want from a
# behavioural reference: a bad state stops loudly instead of being sampled.
BIN_DEFAULT="$WORKSPACE/xenia-canary/build/bin/Linux/Checked/xenia_canary"
BIN_EXE="${CANARY_BIN:-$BIN_DEFAULT}" BIN_EXE="${CANARY_BIN:-$BIN_DEFAULT}"
ISO="${SYLPHEED_ISO:-$WORKSPACE/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso}" ISO="${SYLPHEED_ISO:-$WORKSPACE/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso}"
[ -f "$ISO" ] || { echo "ABORT: no ISO at '$ISO' — set \$SYLPHEED_ISO"; exit 4; } [ -f "$ISO" ] || { echo "ABORT: no ISO at '$ISO' — set \$SYLPHEED_ISO"; exit 4; }

View File

@@ -24,7 +24,18 @@ set -u
# Paths derive from where this script sits — see the note in run-canary-safe.sh. # Paths derive from where this script sits — see the note in run-canary-safe.sh.
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE="$(cd "$HERE/../.." && pwd)" WORKSPACE="$(cd "$HERE/../.." && pwd)"
BIN_DEFAULT="$WORKSPACE/xenia-canary-native/build/bin/Linux/Release/xenia_canary" # 🔴 THE INSTRUMENTED BINARY IS THE `Checked` ONE, and it is the only config this
# build tree can produce: `ninja -n bin/Linux/Release/xenia_canary` lists ZERO
# steps, so Release is not a target here and its binary still dates from
# 2026-08-28, before the audit_61 probe existed. Pointing at Release therefore
# runs an oracle with no probe and no way to refresh it.
#
# The old default named `xenia-canary-native/`, a directory that does not exist
# on this workspace at all -- the checkout is `xenia-canary/`.
#
# `Checked` is optimised WITH assertions, which is what you want from a
# behavioural reference: a bad state stops loudly instead of being sampled.
BIN_DEFAULT="$WORKSPACE/xenia-canary/build/bin/Linux/Checked/xenia_canary"
BIN_EXE="${CANARY_BIN:-$BIN_DEFAULT}" BIN_EXE="${CANARY_BIN:-$BIN_DEFAULT}"
ISO="${SYLPHEED_ISO:-$WORKSPACE/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso}" ISO="${SYLPHEED_ISO:-$WORKSPACE/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso}"
[ -f "$ISO" ] || { echo "ABORT: no ISO at '$ISO' — set \$SYLPHEED_ISO"; exit 4; } [ -f "$ISO" ] || { echo "ABORT: no ISO at '$ISO' — set \$SYLPHEED_ISO"; exit 4; }

View File

@@ -57,7 +57,22 @@ rm -f xenia.log
# Binary is overridable (default keeps the historical _i2d snapshot); extra # Binary is overridable (default keeps the historical _i2d snapshot); extra
# cvars pass through via CANARY_EXTRA_ARGS (space-separated, values w/o spaces). # cvars pass through via CANARY_EXTRA_ARGS (space-separated, values w/o spaces).
# ⚠️ THIS WINE BUILD HAS NO INSTRUMENTATION AND CANNOT BE REFRESHED HERE.
# Measured 2026-09-20: `xenia_canary_i2d.exe` does not exist at all, and the
# `xenia_canary.exe` that does (2026-06-19) has ZERO hits for both
# `audit_61_branch_probe_pcs` and `RE-INPUT`/`RE-DRAW`. Rebuilding it needs the
# clang-cl + xwin cross toolchain on the `cross-build-wine` branch.
#
# 🔴 FOR PROBE RUNS USE `run-canary-native-safe.sh` INSTEAD -- same hard Vulkan
# gate, same headless behaviour, and it runs the Checked native binary, which
# carries audit_61 and the RE-INPUT/RE-DRAW logging.
BIN_EXE="${CANARY_BIN:-xenia_canary_i2d.exe}" BIN_EXE="${CANARY_BIN:-xenia_canary_i2d.exe}"
if [ ! -f "$BIN/$BIN_EXE" ]; then
echo "ABORT: no Wine binary at $BIN/$BIN_EXE" >&2
echo " This build is stale and uninstrumented; use run-canary-native-safe.sh" >&2
echo " for anything needing audit_61 or the RE-* logging." >&2
exit 4
fi
args=(--log_level=3 --mute=true) args=(--log_level=3 --mute=true)
[ -n "$PROBES" ] && args+=("--audit_61_branch_probe_pcs=$PROBES") [ -n "$PROBES" ] && args+=("--audit_61_branch_probe_pcs=$PROBES")
if [ -n "${CANARY_EXTRA_ARGS:-}" ]; then if [ -n "${CANARY_EXTRA_ARGS:-}" ]; then