docs: the UI decode's own evidence images were unreachable -- 11 links repaired

The brief's rule is to commit reference data beside the finding so the
port can be built without a disc. Nothing had ever checked that the docs'
cited artifacts actually exist. doc_link_check.py walks every markdown
file under docs/, resolves each relative link, and reports targets that
are missing -- and separately targets that resolve to a ZERO-BYTE file,
which looks fine in any listing.

  links resolving   1038 -> 1049
  missing targets     16 -> 5
  empty targets        0 -> 0

+11 resolving and -11 missing against 11 edits: the counts pair, which is
the confirmation the pass did what it claimed and touched nothing else.

Two of the sixteen were the evidence for the UI layout decode itself.
structures/ui-rat-layout.md is what the port is built on, and its two
figures -- backing "the tutorial PAUSE menu rebuilds pixel-accurately
from its sprites" and "the same method reproduces the main menu" -- were
written as captures/ui-layout/... from a file in structures/, one
directory too shallow. The headline evidence for the decode could not be
opened from its own document.

Eleven links had the wrong relative depth with the target present. Each
was rewritten only where exactly one candidate path resolved, so nothing
was guessed; the first pass left three alone because equivalent spellings
(captures/../captures/x) failed to collapse, and a second pass normalised
them.

Five remain genuinely absent and are left rather than invented: two point
at MEMORY.md outside the repo, one at a header in the separate
xenia-canary-native tree, and two name documents that were never written
(weapon-datasheet-runtime.md, canary-build-verified-env-confound.md).
None is port-relevant. A missing document is a different problem from a
bad path and is not something a link fix should paper over.
This commit is contained in:
Sylpheed RE agent
2026-08-29 02:34:16 +00:00
parent 7373035868
commit aaaa08b164
12 changed files with 136 additions and 11 deletions

View File

@@ -82,7 +82,7 @@ names had no text to resolve *to*, and the 100 % figure was unreachable.
* **Which recording plays.** The caption is the *text*; the voice bank binding
is a separate and still-unresolved question — see
[voice-bank-leading-region.md](voice-bank-leading-region.md) and the
[voice-bank-leading-region.md](../voice-bank-leading-region.md) and the
known case of a generic line playing against a specific subtitle.
* **Which page a `MSG_DEMO_*` id belongs to.** The `DEMO` family is not called
from the stage scripts at all — 78 of its ids are multi-page, so something

View File

@@ -270,7 +270,7 @@ result of the two.
## ✅ 2026-08-23 (third pass) — the counter is at `0xbdb59668` again, and the refutation above is *refined*, not reversed
Run 4, with the hunt automated end to end
([`ob_hunt.py`](../../tools/re-capture/ob_hunt.py) + the HUD reader), produced
([`ob_hunt.py`](../../../tools/re-capture/ob_hunt.py) + the HUD reader), produced
**exactly one** surviving address:
```
@@ -322,7 +322,7 @@ With the per-entity searches refuted at both word and bit level
counter. It sits at `0xbdb59668`, inside the entity-heap window, so the answer is
readable directly: sample ±0x200 around it across one transition and keep the
words that move **with** it
([`ob_neighbourhood.py`](../../tools/re-capture/ob_neighbourhood.py),
([`ob_neighbourhood.py`](../../../tools/re-capture/ob_neighbourhood.py),
[`captures/ob-counter-neighbourhood-stage02.json`](../captures/ob-counter-neighbourhood-stage02.json)).
Control first: over an 8-second interval while the counter sat still, **0 of the

View File

@@ -40,7 +40,7 @@ resolves all twenty-four:
| `stage\StageParameter_Test.tbl` | 1 — the developer stage |
**That is exactly the 24, and it explains 24 against the 29 stage records**
([`challenge-mission-gate.md`](challenge-mission-gate.md) counts 29:
([`challenge-mission-gate.md`](../challenge-mission-gate.md) counts 29:
16 story + 6 tutorial + 6 challenge + `Test`): the six tutorials do not get one
table each, they **share a single `_Tutorial` table**, and `_Test` accounts for
the last. 16 + 6 + 1 + 1 = 24.

View File

@@ -4,7 +4,7 @@
**reassembled from the disc**: the tutorial PAUSE menu rebuilds pixel-accurately from its
sprites placed at the coordinates in their `.rat` records — no fitting, no manual nudging.
![real vs rebuilt](captures/ui-layout/pause-tutorial-real-vs-rebuilt.png)
![real vs rebuilt](../captures/ui-layout/pause-tutorial-real-vs-rebuilt.png)
*Left: the running game (Canary screenshot). Right: rebuilt from `GP_PAUSE_MENU.pak` alone.
The remaining differences are the animated frame/glow sprites (`*eff*`) that were not
@@ -157,7 +157,7 @@ game, twice, in that order — the records were never fitted to the picture.
The same method run against `GP_TITLE.pak` reproduces the **main menu**, which is a
different screen with a different item count and a different pitch:
![main menu real vs rebuilt](captures/ui-layout/title-mainmenu-real-vs-rebuilt.png)
![main menu real vs rebuilt](../captures/ui-layout/title-mainmenu-real-vs-rebuilt.png)
`ptbtn01..05.rat` give X = 542 for all five and Y = 162 / 242 / 322 / 402 / 482 — an
**80 px** pitch, where the pause menu used 70. Measured against the screenshot, the sprite

View File

@@ -507,7 +507,7 @@ run scan that builds the candidate list — which does not emit a start for thes
resources' real vertex buffers. That is where the remaining 51 live.
The ignored test
[`mesh_consistency_disc.rs`](../../crates/sylpheed-formats/tests/mesh_consistency_disc.rs)
[`mesh_consistency_disc.rs`](../../../crates/sylpheed-formats/tests/mesh_consistency_disc.rs)
still asserts the target state and now records 63 rather than 125; the remaining
cases are where the neighbourhood is itself wrong or absent.