Commit Graph

6 Commits

Author SHA1 Message Date
sylph-decoder
5938d3b1fc notes: build-canary points at a source root that does not exist here
Recorded as a blocker rather than worked around, because it changes what the
next session can plan.

The faithful-capture route is an internal tap at SDLAudioDriver::SubmitFrame,
which receives exactly frame_size_ bytes of the guest s own frame in guest order
with no wall clock in the loop. A cvar-gated WAV writer there would record what
the guest PRODUCED rather than what a device CONSUMED, so it would be gap-free
however slowly the emulator runs -- which is precisely the defect that made both
ADV captures unusable.

The change is small. The build is not. build-canary builds
${PROJECT_DIR:-/work}/xenia-canary, which does not exist in this container; the
source is at /canary. The warm 235 MB tree at /sylph-home/re/canary-build is
configured with CMAKE_HOME_DIRECTORY=/work/xenia-canary, also missing, and its
build-Release.ninja carries no per-file rules -- it re-runs CMake first, and that
reconfigure fails on the absent root. So any Canary change is a full reconfigure
against /canary plus a full compile, at SYLPH_JOBS=4 on a box sitting at about
700 MB free with a documented history of full-parallel builds OOM-killing the
host.

Not attempted: that is a whole session s risk for one probe, and the next session
should decide with the cost in front of it rather than discover it halfway
through.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
2026-08-29 16:22:13 +00:00
sylph-decoder
105a1f65e4 notes: run-canary is silent TWICE over, and the 6-channel stream is a red herring
Completes an entry committed an hour ago that was incomplete, which is worse
than absent because it looked authoritative. Fixing SDL_AUDIODRIVER alone still
records silence: run-canary also passes --mute=true on its own command line
(line 98). With the driver fixed and the mute left alone, Canary attaches a
healthy 6-channel stream, holds it at 100 percent volume, reports Corked: no,
and emits nothing. Both layers have to go, and "$@" is last so --mute=false on
the caller s side wins.

Also records that parec defaults to stereo/44.1 kHz and will resample a
6-channel monitor without saying so -- the first successful-looking capture came
back 2ch 44100 from a 6ch sink.

And a red herring I nearly published as a finding. pactl shows Canary s stream
as float32le 6ch 48000Hz with a full 5.1 channel map, which reads as the guest
requesting 5.1 and would have been strong support for the hypothesis that a
voice cue s three streams are 5.1 channel pairs. It is not evidence about the
game at all: AudioDriver::kFrameChannelsDefault is a hardcoded 6, and the code
path actually used, SDLAudioSystem::CreateDriver(index, semaphore, &driver),
constructs SDLAudioDriver(semaphore) taking every default. The format is
Xenia s; only the content of those six channels is the guest s.

That is the same failure this corpus recorded in METHOD earlier today -- the
specific observation and the general rule reading identically -- caught this
time before it was written down rather than after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
2026-08-29 15:56:24 +00:00
sylph-decoder
a8a828644a notes: run-canary is silent by default, which records perfect silence
Both of these cost time in one session and both are the kind that look like
success.

run-canary line 82 is SDL_AUDIODRIVER=${SDL_AUDIODRIVER:-dummy}, and its own
header explains why: --apu=nop stalls the guest in the intro movie, so the SDL
driver against a dummy device is what lets the title advance. But the comment s
premise -- "there is no PulseAudio here" -- stopped being true when
tools/audio-capture landed, since that starts a daemon on demand. So a capture
through the null sink records pure silence, of the right length, behind a run
that looks perfectly healthy. The override is
PULSE_SINK=cap SDL_AUDIODRIVER=pulseaudio run-canary, and the live check is
pactl list sink-inputs: empty means Canary never attached and the sink sits at
IDLE. audio-capture s own -inf peak warning is the backstop, but it only fires
after the whole run.

Separately, pkill -f and pgrep -f match the caller s OWN command line. Hit twice
here: pkill -9 -f adv_audio_cap.sh killed the shell running it, and an
until ! pgrep -f "probe.py --run" loop never exited because the loop s own
command line contained the pattern -- which looks exactly like the job hanging.
Kill by process name with ps -o pid= -C instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
2026-08-29 15:52:05 +00:00
MechaCat02
f5315ddf59 decoder: tell it about the reference assets, and that the DB can be wrong
The mounts landed but the agent could not learn of them: I documented them in
CONTAINER-NOTES.md, which the decoder's prompt does not list, and then restarted
the container -- so a fresh session with no memory of the exchange had a 586 MB
database and a decompressed image sitting unmentioned in its filesystem.

Now in the PROMPT itself, not only in a document, because the prompt is the one
thing a new session is guaranteed to read. CONTAINER-NOTES.md is also added to
its reading list.

And the caveat that matters more than the asset. The .pe is PRIMARY -- the bytes
the console executed. The database is somebody's ANALYSIS of them, produced by a
disassembler that had to guess, and it is wrong in the ways disassemblers are
wrong: misdecoded mnemonics where data was read as code, function boundaries
short or long or merged or split, coverage missing entirely for code reached only
by indirect dispatch, and names that are derived rather than symbols.

So a finding resting on a database row is not established until the bytes agree:
read the same address out of the .pe and check. Where they disagree the image
wins, and the disagreement is itself worth recording, because it tells the next
reader which parts of the database to distrust.

A fast index into 9.2 MB of machine code, not a source of truth.
2026-08-29 15:23:47 +02:00
MechaCat02
d2d2592c92 decoder: mount the disassembly DB and the flat VA image
The decoder had neither, and reported the gap precisely: four scripts in this
repo READ /work/xenia-rs/sylpheed.db and nothing produces it, so the whole static
PPC route was consumers with the producer missing.

Both exist on the host and are now mounted read-only: the 586 MB database (25 481
functions, 851 classes with RTTI, EH tables, imports, 1.8M indirect-dispatch
candidates) and the decompressed image.

The image is the more useful of the two. It is a FLAT VA DUMP -- file offset =
VA - 0x82000000 -- so reading a known address needs no XEX decrypt, no LZX, and
no booted emulator. The decoder had independently recovered the same bytes by
dumping /dev/shm/xenia_memory_* and validating against the GamePart table, which
is good work and a sound method, but it noted itself that needing a running
emulator is a bad dependency for something the entire static corpus rests on. It
does not need one.

Also recorded that an earlier claim the .pe was STALE was tested and refuted, so
nobody re-litigates it, and that instructions.raw is an INT rather than hex.

Written down as reference material, explicitly NOT a deliverable: they are
read-only, they come from outside the repository, and a fresh checkout elsewhere
has neither. Reimplementing the producer belongs in sylpheed-formats, and until
it exists every static finding rests on an artefact this project cannot rebuild.
2026-08-29 15:17:52 +02:00
MechaCat02
c58196b795 containers: each agent clones the monorepo into its own volume
The last structural fix for the collision class that has bitten three times. Both
containers now clone the repository into their OWN named volume instead of
bind-mounting a human's working tree, so an agent's local git config cannot
capture a human's commits, a credential helper cannot leak a container-only path
onto the host, and a `git add -A` cannot sweep another party's in-flight files.

Cloned once at startup and never auto-pulled: pulling under a running agent
moves files out from under whatever it is mid-edit, which is the same bug again.

Accepted knowingly: Claude Code keys per-project memory off the working
directory, so moving off the host path starts that memory empty. The corpus in
docs/ is the memory that matters and it travels with the clone.

Other changes:
* docker/agent -> docker/decoder; the launcher is sylph-decoder. Roles, not
  "the agent", now that there is more than one.
* /reborn is gone -- one repository now, so the port reads HANDOFF from its own
  checkout rather than through a live read-only mount of someone else's tree.
* Canary mounts separately at /canary; it stays a fork tracking upstream.
* A shared `sylpheed-exchange` volume at /exchange, with tools/ on PATH so
  `share` is available in both.
* The decoder's credential file gets the .host-copy treatment the port already
  had -- `credential.helper=store` rewrites by rename-over-target, which is
  EBUSY on a bind mount and reports a fatal that is not one.
* Budget split deliberately: decoder 5 cpu / 6 GB, port 3 / 4, leaving room for
  the planned Referee. "Half the host" was right when there was one agent.

Prompts move to docs/agents/ and are rewritten around the protocol: the oracle
is the running game, dynamic RE stays with the decoder, each iteration must
attempt to refute one claim of the other, and neither may verify its way out of
its own role.
2026-08-29 11:48:30 +02:00