docs: the title does act on (A) - the loader thread it spawns is what stalls

"The title screen ignores (A)" is withdrawn. First-divergence across three boots
of the same binary says otherwise.

A slot-(1F) guest thread is spawned BY the press: exactly once per run,
immediately after the keydown, same stack base 70880000-70900000 in both runs
that got one, and never at all in the run that never accepted a press - which
rules out a periodic worker starting around the same time.

  prm6, reached the menu:      (A) at line 6498, (1F) at 6500, 6 ResolvePath after
  opt2, stuck on the title:    (A) at line 1287, (1F) at 1288, 0 ResolvePath after
  opt,  stalled before title:  no (A) ever, no (1F) thread at all

In the successful run the loader immediately reads six paths out of the on-disc
cache and the menu appears. In the failed run the same thread starts and performs
no file I/O ever again. Total ResolvePath for the three boots is 90/84/78 - the
successful run's extra six are exactly the ones after the press, so the boots are
otherwise identical in I/O.

Refuted as the cause: the cache-flush std::out_of_range. All four of today's runs
have zero GUEST-THROW, zero CRASH DUMP and zero Access Violation, and the guest
stays alive throughout with its keystroke-poll counter climbing past 15000.

Next probe is neither input nor the crash: what the (1F) thread waits on.
This commit is contained in:
Sylpheed RE agent
2026-08-19 10:03:41 +00:00
parent 7927f6c3b7
commit f50776473b
3 changed files with 1495 additions and 4 deletions

View File

@@ -143,10 +143,17 @@ See [`structures/ui-composable-bundles.md`](structures/ui-composable-bundles.md)
measured screen with a tied group — not more static guessing at fields that
have all now been checked. 🔴 **Attempted 2026-08-19 and blocked:** advancing
past the title is intermittent — **1 success in 3 attempts**, same binary,
same profile, same procedure. Input is excluded for the third time (every
press logged as delivered, `vk=5800`/`vk=5814`); one failure stalled *before
the title was built at all*, with the guest alive and polling and only the
splash object resident. Next probe must be guest-side, not input-side.
same profile, same procedure. **And now diagnosed one layer deeper:** the
title *does* act on Ⓐ — the press spawns a slot-`(1F)` loader thread (exactly
once per run, at the keydown, never in a run that got no press). In the
successful boot that thread immediately reads six paths from the on-disc cache
and the menu appears; in a failed boot it starts and issues **no file I/O
ever**. So "the title ignores Ⓐ" is **withdrawn** — the loader stalls.
🔴 Refuted as the cause: the cache-flush crash. All four of today's runs have
**zero** `GUEST-THROW`, `CRASH DUMP` and `Access Violation`; the guest stays
alive and polling. **Next probe:** what the `(1F)` thread waits on — an event
or file-system trace on that thread, not input, not the crash.
See [`canary-scripted-input-traps.md`](canary-scripted-input-traps.md).
***Blend mode.** Everything is straight alpha-over. The near-white flash
quads (`0xf0ffffff`) and coloured ones (`0x60ff0000`) may be additive. The
title capture cannot separate the two — its resting elements are all

View File

@@ -381,3 +381,66 @@ Also confirmed on the way: the screen after NEW GAME is **DIFFICULTY** — its
whole-image signature `(9.7, 19.5, 53.9)` matches the earlier capture of that
screen exactly — so the menu path is understood even though the run dies after
it.
## ✅ The title *does* act on Ⓐ — what stalls is the loader thread (2026-08-19)
**Status:**`CONFIRMED` by first-divergence across three boots of the same
binary. 🔴 The stall itself is unexplained. ⚠️ **"the title screen ignores Ⓐ" is
withdrawn** — it was the wrong description of the symptom, and it sent two
iterations looking at the input path, which is fine.
Advancing past the title is intermittent — **1 success in 3** on 2026-08-19 with
the same binary, profile and procedure — and the input path had already been
excluded three ways (the press is logged as delivered to the guest). Comparing
the successful boot against the failed ones line by line says what actually
differs.
**A slot-`(1F)` guest thread is spawned by the press.** It starts exactly once
per run, immediately after the Ⓐ keydown, at the same stack base
(`70880000-70900000`) in both runs that got one:
| run | Ⓐ delivered at line | `(1F)` thread starts | `ResolvePath` calls after |
|---|---|---|---|
| `prm6`**reached the menu** | 6498 | 6500 | **6** |
| `opt2` — stuck on the title | 1287 | 1288 | **0** |
| `opt` — stalled before the title | never | **never starts** | — |
The run that never accepted a press never creates a `(1F)` thread at all, which
rules out "a periodic worker that happens to start around then".
So the game **does** act on the button: it spawns its loader. In the successful
run that loader immediately reads six paths out of the on-disc cache —
```
HostPathDevice::ResolvePath(\aab216c3\a\c7e701e)
HostPathDevice::ResolvePath(\d5faa9db\e\b80b1a0)
HostPathDevice::ResolvePath(\d5faa9db\c\9f11f8b)
```
— and the menu appears. In the failed run the same thread starts and performs
**no file I/O ever again**
([log](captures/title-loader-thread-stalls.log)). Total `ResolvePath` counts for
the three boots are 90 / 84 / 78: the successful run's extra six are exactly the
ones after the press, so the boots are otherwise identical in I/O.
### Refuted: it is not the cache-flush crash
The obvious suspect was the `std::out_of_range` in the cache flush
([`title-crash-stl-tree.md`](title-crash-stl-tree.md)), which that page shows an
incomplete on-disc cache triggers ~100 s into a boot. **All four of today's runs
have zero** `GUEST-THROW`, zero `CRASH DUMP` and zero `Access Violation`. The
guest is alive throughout — RSS steady, the keystroke-poll counter climbing past
15 000 — it simply stops loading.
### Where the next probe goes
Not at input, and not at the crash. At the `(1F)` thread: it is created, it is
scheduled (the guest keeps running), and it issues no I/O. Whatever it waits on
first is the thing to find — a `KeSetEvent` / `NtWaitForSingleObject` trace on
that thread, or the file-system call it never reaches.
Worth noting for anyone scripting boots meanwhile: **retry whole boots**. Nothing
about the failed state recovers — four further presses over four minutes changed
nothing, which is consistent with a loader that is stuck rather than a screen
that is not listening.

File diff suppressed because it is too large Load Diff