Compare commits

...

8 Commits

Author SHA1 Message Date
MechaCat02
147767c49d docs(port): the two steps a host needs that the container hides
Some checks failed
CI / Native — linux (pull_request) Failing after 1h21m4s
CI / WASM — Web (pull_request) Successful in 29m43s
CI / Formatting (pull_request) Successful in 24s
Both were found by following RUNNING.md on this host from a clean checkout, and
both fail in a way that blames the wrong thing.

`godot --path port` without a prior `--import` produces THIRTY parse errors
naming half the project -- ScreenView, MenuAudio, Gamepad, MenuFlow, ExportTree
all "not declared", each cascading into "cannot infer the type of ..." -- and
then hangs. That reads like a port that does not compile. It is one gitignored
file: `port/.godot/global_script_class_cache.cfg`, which Godot writes at import
time and which `class_name` resolution needs. After `--headless --path port
--import`: zero errors, `main_menu` loads 16 elements and settles at t=56.

The exporter shells out to ffmpeg and ffprobe for the movies AND the audio, and
`sylph-ci:local` has neither -- so the documented `cargo run` fails minutes in,
after the screens are already written and it looks like it is working. The image
to use is `sylph-ci:ffmpeg`.

Neither bites in the agent container: its image carries an imported tree and its
own ffmpeg. That is exactly why neither was ever written down.

Also recorded: `--headless` CANNOT `--capture` (the dummy renderer never emits
`frame_post_draw`; the port detects this and says so), and `xvfb-run`, which its
message suggests, is not installed on every host -- it is not on this one.

Measured 2026-09-26 on fabi-Hyrican-PC: 31 s build, 4 min 11 s export, 149 MB,
`sylpheed-export check` validates all 16 screens against sylpheed.screen/3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-26 17:56:52 +02:00
bfe590c6bc Merge pull request 'fix(port): check-citations was red, and its selftest BROKEN, on a clean tree' (#66) from fix/citations-generated-tree into main
Some checks failed
CI / Native — linux (push) Failing after 1h2m47s
CI / WASM — Web (push) Successful in 24m42s
CI / Formatting (push) Successful in 25s
Reviewed-on: #66
2026-09-21 16:33:53 +00:00
MechaCat02
7907c8d286 fix(port): check-citations was red, and its selftest BROKEN, on a clean tree
Some checks failed
CI / Native — linux (pull_request) Failing after 1h3m26s
CI / WASM — Web (pull_request) Successful in 25m21s
CI / Formatting (pull_request) Successful in 28s
Two independent defects, both making the script report a correct checkout as
wrong. Neither is new; both were invisible because nobody ran it here.

1. `export/` is the exporter's OUTPUT and is gitignored (`/export*/`). A
   checkout where nobody has run the exporter has no `export/` at all, so the
   four `DECISIONS.md`/`BLOCKED.md` citations of `export/manifest.json` and
   `export/screens/...` landed in "resolve NOWHERE" and the check exited 1 --
   red for a state no edit can fix, which is the exact shape its own docstring
   says it exists to avoid. `check-capture-citations` learned this for
   `docs/re/captures/`; same rule now: absent BECAUSE UNBUILT is reported,
   absent while the tree IS built still fails. Verified both ways -- `mkdir
   export` and the same four go back to failing.

2. The selftest's peer-branch fixture cited
   `docs/re/f5-a-press-snaps-the-plate.md`, which the consolidation made an
   ordinary local file. The fixture stopped testing the scanner and started
   reporting it broken; `PEER_REFS` also still named
   `origin/auto/frame-blend-draw-path`, a branch that no longer exists. The
   selftest now FINDS a peer-only path at runtime, and where none exists -- the
   normal case on a clean checkout, measured: zero -- it says the class is empty
   here rather than claiming a failure. The class itself stays: the next topic
   branch that lands a finding recreates the condition exactly.

Measured: check 123 citations, 119 resolve, 4 unbuilt, 0 nowhere, exit 0 (was
exit 1). Selftest ok (was 🔴 BROKEN, on `main` too). The new generated-tree case
was confirmed to FAIL against the unfixed function first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 18:17:11 +02:00
c03e5c9bd0 Merge pull request 'fix(re): the capture-citation check was red on a clean tree' (#65) from fix/captures-root-citation into main
Some checks failed
CI / Native — linux (push) Has been cancelled
CI / WASM — Web (push) Has been cancelled
CI / Formatting (push) Has been cancelled
Reviewed-on: #65
2026-09-21 16:13:53 +00:00
MechaCat02
43f0a905a0 fix(re): the capture check was red on a clean tree
Some checks failed
CI / Native — linux (pull_request) Failing after 1h1m57s
CI / WASM — Web (pull_request) Successful in 24m51s
CI / Formatting (pull_request) Successful in 28s
`committed()` built the directory set from `range(4, len(parts))`. Index 3 is
`docs/re/captures` itself, so every SUB-directory was registered and the root
never was. Eight pages cite the directory as a whole -- PROTOCOL's evidence
table, CONSOLIDATION, CONTAINERS-AND-AGENTS, decoder-loop, both HANDOFFs,
BLOCKED, verify-screen-blend-divergence -- and each of those looked like a
citation of a file that does not exist. `check-capture-citations` exited 1 on a
correct checkout, which is the failure mode its own comments warn about: "a
gate that is red on every clean checkout" teaches people to ignore it.

The selftest could not see this. It takes `next(iter(dirs))`, and an arbitrary
member of that set is always a sub-directory -- the one case that works. It now
asserts the root explicitly, with and without its trailing slash. Both new cases
were confirmed to FAIL against the unfixed function before the fix went in; a
selftest case that passes either way is decoration.

Measured: full check 203 citations, 212 captures, 0 dangling, 0 tracked assets,
exit 0. Before: exit 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 18:12:27 +02:00
f732b5ac1a Merge pull request 'fix(decoder): give the container its database and its instrumented oracle' (#64) from fix/decoder-db-mount 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: #64
2026-09-21 16:10:22 +00:00
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
6 changed files with 193 additions and 18 deletions

View File

@@ -9,6 +9,36 @@ Everything below has been run. Where a number is quoted it was measured in this
container, and where the container distorts it that is said rather than left for container, and where the container distorts it that is said rather than left for
the reader to discover. the reader to discover.
## 0. Import the project once, on a checkout that has never run it
```bash
godot --headless --path port --import
```
🔴 **Skip this and the port looks broken rather than unimported.** Godot resolves
`class_name` through `port/.godot/global_script_class_cache.cfg`, which is
gitignored and is written at import time. Without it **every** class fails —
`ScreenView`, `MenuAudio`, `Gamepad`, `MenuFlow`, `ExportTree` — and each failure
cascades into a dozen more:
```
SCRIPT ERROR: Parse Error: Could not find type "ScreenView" in the current scope.
SCRIPT ERROR: Parse Error: Cannot infer the type of "_pad" variable …
ERROR: Failed to load script "res://scripts/boot.gd" with error "Parse error".
```
Thirty-odd parse errors naming half the project reads like a port that does not
compile. It is one missing cache, and the import takes seconds. Measured on a
fresh checkout 2026-09-26: before, 30 errors and a hang; after, **zero**.
The container does not hit this because its image carries an imported tree, which
is exactly why the step was never written down.
⚠️ `--headless` **cannot capture**. Godot's dummy renderer never emits
`frame_post_draw`, so `--capture=` under `--headless` would hang; the port
detects it and says so. A capture needs a display — `xvfb-run -a godot …`, and
note that **`xvfb-run` is not installed on every host**.
## 1. Build the asset tree ## 1. Build the asset tree
The Godot project reads `export/`, never the disc. The Godot project reads `export/`, never the disc.
@@ -17,7 +47,21 @@ The Godot project reads `export/`, never the disc.
cargo run --release -p sylpheed-export -- export --disc /disc --out export cargo run --release -p sylpheed-export -- export --disc /disc --out export
``` ```
Roughly four minutes, most of it transcoding two movies. It **rewrites `export/` Roughly four minutes, most of it transcoding two movies.
🔴 **The exporter needs `ffmpeg` and `ffprobe` on PATH** — it shells out to them
for both the movies and the audio. On a host, that means running it through the
CI container built WITH them:
```bash
SYLPH_CI_IMAGE=sylph-ci:ffmpeg docker/ci/run \
cargo run --release -p sylpheed-export -- export --disc /disc --out export
```
`sylph-ci:local` has neither, and the failure arrives several minutes in, after
the screens have already been written. Measured 2026-09-26 on `fabi-Hyrican-PC`:
31 s to build, 4 min 11 s end to end, 149 MB of `export/`, and
`sylpheed-export check` then validates all 16 screens. It **rewrites `export/`
wholesale** — never hand-edit anything in there; hand-written decisions live in wholesale** — never hand-edit anything in there; hand-written decisions live in
`authored/` beside it, and survive a re-export. `authored/` beside it, and survive a re-export.

View File

@@ -36,7 +36,60 @@ CITE = re.compile(
r"`?((?:docs|crates|port|tools|authored|export)/[\w./-]+" r"`?((?:docs|crates|port|tools|authored|export)/[\w./-]+"
r"\.(?:md|rs|gd|json|txt|py|tsv|csv))`?" r"\.(?:md|rs|gd|json|txt|py|tsv|csv))`?"
) )
PEER_REFS = ("origin/auto/frame-blend-draw-path", "origin/main") # ⚠️ `origin/auto/frame-blend-draw-path` used to head this list and NO LONGER
# EXISTS -- the consolidation merged it and the branch was removed. The class is
# kept because it is about the workflow, not about that one branch: the next
# topic branch that lands a finding recreates the condition exactly. What must
# not happen again is the selftest asserting the class against a fixture path
# that has since become an ordinary local file, which is how it came to print
# 🔴 BROKEN on a correct checkout. It now finds its own fixture, or says the
# condition does not exist here.
PEER_REFS = ("origin/main",)
def a_peer_only_path() -> str | None:
"""A path carried by a PEER_REF but absent from this working tree.
The selftest needs a REAL one: a hardcoded fixture silently stops testing
the moment that file lands locally, and then reports the scanner broken
instead of itself. On a clean, up-to-date checkout there is usually no such
path at all -- which is not a failure, it is the class being empty here.
"""
for ref in PEER_REFS:
out = subprocess.run(["git", "ls-tree", "-r", "--name-only", ref],
capture_output=True, text=True)
for f in out.stdout.splitlines():
if f.endswith((".md", ".rs", ".gd", ".json", ".txt", ".py",
".tsv", ".csv")) and not os.path.exists(f):
return f
return None
def gitignored(path: str) -> bool:
"""Is this path deliberately untracked? Pattern match -- existence not needed."""
return subprocess.run(["git", "check-ignore", "-q", path],
capture_output=True).returncode == 0
def unbuilt(path: str) -> bool:
"""A citation of GENERATED output whose tree has not been built here.
🔴 THE THIRD ABSENCE, AND IT IS NOT AN ERROR. `export/` is the exporter's
output and is gitignored (`.gitignore` `/export*/`). A checkout where nobody
has run the exporter has no `export/` at all, so the four `DECISIONS.md` and
`BLOCKED.md` citations of `export/manifest.json` and `export/screens/...`
were counted as "resolve NOWHERE" and this check was red on a clean tree --
for a state no edit can fix, which is precisely the shape its own docstring
says it exists to avoid.
`check-capture-citations` already learned this for `docs/re/captures/`.
Same rule here: absent BECAUSE UNBUILT is reported; absent while the tree
IS built is a real broken citation and still fails.
"""
if not gitignored(path):
return False
root = path.split("/")[0]
return not os.path.exists(root)
def on_a_ref(path: str) -> str | None: def on_a_ref(path: str) -> str | None:
@@ -49,7 +102,7 @@ def on_a_ref(path: str) -> str | None:
def scan(files): def scan(files):
resolves, peer, nowhere = 0, {}, {} resolves, peer, nowhere, ungenerated = 0, {}, {}, {}
for p in files: for p in files:
try: try:
text = open(p, encoding="utf-8").read() text = open(p, encoding="utf-8").read()
@@ -58,11 +111,13 @@ def scan(files):
for m in sorted(set(CITE.findall(text))): for m in sorted(set(CITE.findall(text))):
if os.path.exists(m): if os.path.exists(m):
resolves += 1 resolves += 1
elif unbuilt(m):
ungenerated.setdefault(m, p)
elif (ref := on_a_ref(m)): elif (ref := on_a_ref(m)):
peer.setdefault(m, (p, ref)) peer.setdefault(m, (p, ref))
else: else:
nowhere.setdefault(m, p) nowhere.setdefault(m, p)
return resolves, peer, nowhere return resolves, peer, nowhere, ungenerated
def main() -> int: def main() -> int:
@@ -82,28 +137,51 @@ def main() -> int:
# exists) and does not resolve here (the reader still gets nothing), and # exists) and does not resolve here (the reader still gets nothing), and
# a scanner that collapsed it into either would make the flag meaningless # a scanner that collapsed it into either would make the flag meaningless
# while still passing the two checks above. # while still passing the two checks above.
peer_probe = a_peer_only_path()
peerfile = os.path.join(tmp, "peer.md") peerfile = os.path.join(tmp, "peer.md")
open(peerfile, "w").write("see `docs/re/f5-a-press-snaps-the-plate.md`\n") open(peerfile, "w").write("see `%s`\n" % (peer_probe or "docs/port/PORT-MISSION.md"))
rp, pp, np_ = scan([peerfile]) # The FOURTH class: generated output whose tree is not built here.
# It has to be told apart from "resolves nowhere", which is the whole
# point -- a scanner that lumped them together is what made this check
# red on a clean checkout.
genfile = os.path.join(tmp, "gen.md")
open(genfile, "w").write("see `export/screens/title/main_menu.json`\n")
_, _, nb = scan([bad]) rp, pp, np_, gp = scan([peerfile])
r, _, ng = scan([good])
_, _, nb, _ = scan([bad])
r, _, ng, _ = scan([good])
rg, pg, ng2, gg = scan([genfile])
caught = len(nb) == 1 caught = len(nb) == 1
passed = len(ng) == 0 and r == 1 passed = len(ng) == 0 and r == 1
peer_ok = len(pp) == 1 and rp == 0 and len(np_) == 0 # None == the class is empty in this checkout, not that it is broken.
ok = caught and passed and peer_ok peer_ok = (len(pp) == 1 and rp == 0 and len(np_) == 0 and len(gp) == 0) \
if peer_probe else None
# Only meaningful while `export/` is absent; if someone ran the exporter
# in this checkout the citation legitimately resolves instead.
gen_ok = (len(gg) == 1 and len(ng2) == 0 and len(pg) == 0) \
if not os.path.exists("export") else (rg == 1)
ok = caught and passed and gen_ok and peer_ok is not False
print("selftest: planted dangling caught=%s, real citation passed=%s, " print("selftest: planted dangling caught=%s, real citation passed=%s, "
"peer-branch classed separately=%s -> %s" "peer-branch classed separately=%s, unbuilt-generated classed "
% (caught, passed, peer_ok, "ok" if ok else "🔴 BROKEN")) "separately=%s -> %s"
if not peer_ok: % (caught, passed,
"n/a (no peer-only path exists here)" if peer_ok is None
else peer_ok,
gen_ok, "ok" if ok else "🔴 BROKEN"))
if not gen_ok:
print(" 🔴 a citation of unbuilt generated output must NOT be "
"dangling; got resolves=%d peer=%d nowhere=%d ungenerated=%d"
% (rg, len(pg), len(ng2), len(gg)))
if peer_ok is False:
print(" 🔴 --for-merge cannot mean anything if the peer class is " print(" 🔴 --for-merge cannot mean anything if the peer class is "
"not distinguished; got resolves=%d peer=%d nowhere=%d" "not distinguished; got resolves=%d peer=%d nowhere=%d"
% (rp, len(pp), len(np_))) % (rp, len(pp), len(np_)))
return 0 if ok else 2 return 0 if ok else 2
files = sorted(glob.glob("docs/port/*.md")) files = sorted(glob.glob("docs/port/*.md"))
resolves, peer, nowhere = scan(files) resolves, peer, nowhere, ungenerated = scan(files)
total = resolves + len(peer) + len(nowhere) total = resolves + len(peer) + len(nowhere) + len(ungenerated)
print("citations of repo paths in docs/port/*.md: %d" % total) print("citations of repo paths in docs/port/*.md: %d" % total)
print(" resolve here : %d" % resolves) print(" resolve here : %d" % resolves)
# 🔴 --for-merge TURNS THE PEER CLASS INTO A FAILURE. # 🔴 --for-merge TURNS THE PEER CLASS INTO A FAILURE.
@@ -130,6 +208,13 @@ def main() -> int:
print(" After this merges they resolve NOWHERE -- the reader gets a dead") print(" After this merges they resolve NOWHERE -- the reader gets a dead")
print(" path. Land the finding first and make it a dependency of this PR.") print(" path. Land the finding first and make it a dependency of this PR.")
return 1 return 1
if ungenerated:
print(" not built in this checkout : %d (reported, not failed)"
% len(ungenerated))
for m, src in sorted(ungenerated.items()):
print(" %-52s <- %s" % (m, os.path.basename(src)))
print(" run the exporter and these resolve; a path still missing")
print(" afterwards IS dangling and fails below.")
if nowhere: if nowhere:
print(" 🔴 resolve NOWHERE : %d" % len(nowhere)) print(" 🔴 resolve NOWHERE : %d" % len(nowhere))
for m, src in sorted(nowhere.items()): for m, src in sorted(nowhere.items()):

View File

@@ -82,10 +82,16 @@ def committed() -> tuple[set[str], set[str]]:
if fn == ".gitignore": if fn == ".gitignore":
continue continue
files.add(os.path.join(dirpath, fn).replace(os.sep, "/")) files.add(os.path.join(dirpath, fn).replace(os.sep, "/"))
# 🔴 START AT 3, NOT 4 — 3 is `docs/re/captures` ITSELF. Starting at 4 built
# every sub-directory and never the root, so the eight pages that cite the
# directory as a whole ("evidence lives in `docs/re/captures/`") each looked
# like a citation of a file that does not exist, and the gate was red on a
# clean, correct tree. The selftest could not see it either: it took an
# arbitrary member of `dirs`, which is always a sub-directory.
dirs = set() dirs = set()
for f in files: for f in files:
parts = f.split("/") parts = f.split("/")
for i in range(4, len(parts)): # docs/re/captures/<dir>/... for i in range(3, len(parts)): # docs/re/captures[/<dir>/...]
dirs.add("/".join(parts[:i]) + "/") dirs.add("/".join(parts[:i]) + "/")
return files, dirs return files, dirs
@@ -182,6 +188,9 @@ def selftest() -> int:
("real file passed", real_file, True), ("real file passed", real_file, True),
("directory reference passed", real_dir, True), ("directory reference passed", real_dir, True),
("directory without slash passed", real_dir.rstrip("/"), True), ("directory without slash passed", real_dir.rstrip("/"), True),
# The root is the case the arbitrary `real_dir` above can never be.
("captures root itself passed", ROOT, True),
("captures root without slash passed", ROOT.rstrip("/"), True),
("sentence punctuation stripped", real_file + ".", True), ("sentence punctuation stripped", real_file + ".", True),
] ]
# 🔴 THE SELFTEST USED TO PASS WHILE THE SCAN RETURNED NOTHING. It exercised # 🔴 THE SELFTEST USED TO PASS WHILE THE SCAN RETURNED NOTHING. It exercised

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