Source changes (dormant parity infra, retained from iterate 2.AI/2.AO): - xenia-kernel/exports.rs: nt_create_event manual_reset polarity + related event wiring - xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps (.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as regenerable local artifacts — see memory + HANDOFF for the running findings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.5 KiB
Phase C+11.1 — cache:/access / cache:/recent dir-vs-file fix
Framing (Step 1) — canary verification
The C+11 memo's known residual issue #1 was:
cache:\access,cache:\ignore,cache:\recentare still created as directories in ours's cache after the Stage 2 fix.
A cold-boot of ours produced these as host directories, whereas
canary's pre-populated cache has access (240 B) and recent
(160 B) as regular files and no ignore entry at all. The
C+11 code comment at exports.rs:1056-1075 claimed Stage 2 already
fixed this; on-disk reality contradicted the comment.
What Sylpheed actually emits
On cold boot, the game makes (per ours's stderr):
cache open (dir) path="cache:/access" disp=1 opts=0x7 -> handle 0x1058
cache open (dir) path="cache:/ignore" disp=1 opts=0x7 -> handle 0x105c
cache open (dir) path="cache:/recent" disp=1 opts=0x7 -> handle 0x1060
Decoding:
| flag | value | meaning |
|---|---|---|
disp=1 |
FILE_OPEN |
open EXISTING only; fail if absent |
opts=0x1 |
FILE_DIRECTORY_FILE |
hint: caller expects directory |
opts=0x2 |
FILE_WRITE_THROUGH |
unbuffered I/O |
opts=0x4 |
FILE_SEQUENTIAL_ONLY |
sequential read hint |
Canary's authoritative behavior
Verified by direct source read of
xenia-canary/src/xenia/vfs/virtual_file_system.cc:265-273:
switch (creation_disposition) {
case FileDisposition::kOpen:
case FileDisposition::kOverwrite:
// Must exist.
if (!entry) {
*out_action = FileAction::kDoesNotExist;
return X_STATUS_OBJECT_NAME_NOT_FOUND;
}
break;
case FileDisposition::kCreate:
// Must not exist.
...
Canary returns X_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000034)
before any CreatePath call. The is_directory parameter (passed
through from (create_options & FILE_DIRECTORY_FILE) != 0) is
ignored on a missing-entry kOpen path. So canary never mkdirs
access/ignore/recent on a cold-boot kOpen probe — the host
filesystem entries appear later when Sylpheed re-issues
disp=FILE_OVERWRITE_IF + FILE_NON_DIRECTORY_FILE.
Ours's bug (pre-fix)
In open_cache_file (exports.rs:1077-1098 of the C+11 HEAD):
let is_dir_open = host_exists_as_dir
|| (!host_exists_as_file && !want_non_dir && want_dir);
if is_dir_open {
if want_dir && !host_path.exists() {
if let Err(e) = std::fs::create_dir_all(host_path) {
...
}
}
// SUCCESS branch follows
The create_dir_all call ran whenever want_dir && !host_path.exists() regardless of disposition. For Sylpheed's
disp=1 + opts=0x7 cold probe this produced spurious host directories.
Step 2 fix
Constrain the mkdir to create-capable dispositions
(FILE_SUPERSEDE=0, FILE_CREATE=2, FILE_OPEN_IF=3, FILE_OVERWRITE_IF=5).
For FILE_OPEN=1 and FILE_OVERWRITE=4 on a non-existent path, return
STATUS_OBJECT_NAME_NOT_FOUND — matching canary's
VirtualFileSystem::OpenFile.
Patch: see fix.diff.
Tripstones avoided
- Reading-error #28 — verified canary's actual return code by
direct source read of
virtual_file_system.cc:265-273, not by docs lookup. - Canary cache backup: 23-file / 4.8 MB oracle preserved at
canary-cache-pre-wipe.tar.gz(4.7 MB compressed) before any wipe. Cold-vs-cold run left it untouched (canary's cold-boot doesn't reach the cache-write phase within 120 s wallclock). --mute=trueused on the canary cold-vs-cold run.- Renamed binaries:
xrs-c11p1/xc-c11p1.exeto dodge the project Stop hook.