Skip to content

docs: rewrite the docs: player docs in docs/user/, developer docs in docs/dev/, the readme as a front page - #2561

Open
Joey-Magnus wants to merge 9 commits into
NotRexed:mainfrom
Joey-Magnus:docs/rewrite
Open

Joey-Magnus wants to merge 9 commits into
NotRexed:mainfrom
Joey-Magnus:docs/rewrite

Conversation

@Joey-Magnus

@Joey-Magnus Joey-Magnus commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

hckrman101 asked in #dev-work for the docs to be rewritten, split into docs for players and docs for developers, with the junk cleaned out. This is that rewrite.

Drafted by _trash.panda's agent; _trash.panda reviewed the outline and the first page and set the style.

The new layout

readme.md is now a front page (148 lines, was ~900): what Hookshot is, a short status, getting started, an index of the docs, how it works, credits and license. It still ships in the AppImage.

docs/user/, for players:

Page
install.md Requirements, the Windows and Linux builders with their current labels and options, the first start, updating and update tracks, portable mode, where files go, uninstalling
settings.md Every menu row on main, by tab, with its choices, default and what it does. The readme's table was missing about 22 rows
upscaling.md Which upscaler to pick, getting DLSS and FSR 4, frame generation, Reflex and Anti-Lag 2
controls.md Default keys and controller layout, rebinding, mouse, motion, rumble, Steam Input
saves.md Save locations, backups, moving saves to and from emulators (was docs/saves.md)
mods.md Installing mods, order, and the resource size table
troubleshooting.md What to put in a bug report, then problems by symptom, the Linux udmabuf fix, low FPS, the AMD texture workaround

Screenshots (docs/images/user/): the Linux builder and each menu tab, taken from a headless run of a game built from current main, with default settings and no mods.

docs/dev/, for developers: README.md (index), building.md, running.md, switches.md, automation.md (was docs/control.md), architecture.md, testing.md, releasing.md (was docs/versioning.md), workflow.md (was docs/workflow.md), plus cpu-profile.md, portable-builder.md and writeback-observations.md moved in unchanged.

CONTRIBUTING.md is cut to the PR rules and links (111 lines, was ~300). AGENTS.md points at docs/dev/.

Also moved into docs/dev/ unchanged: windows-builder-runtime.md. Unchanged: docs/worklist.md, docs/tested-configurations.md, CHANGELOG.md (one path in its hand-written intro updated), BAKE.md.

What went

  • The 33 one-topic notes in docs/ were PR write-ups committed beside their fixes. Nothing referenced them. The facts worth keeping are in architecture.md, switches.md and building.md. The three rules most of them repeat are now stated once in architecture.md: mapped memory isn't assumed coherent, a failed Vulkan creation is never cached, and GPU lifetimes come from fences and the timeline, not frame counts.
  • design.md's dated plan and lab-notebook sections (§5-6, §8-12). Its architecture and replay rules are in architecture.md.
  • Build setup that existed in three copies (readme, CONTRIBUTING, development.md) now exists once, in building.md.
  • History, issue numbers and anecdotes in player-facing text.

Stale facts fixed along the way

  • The Linux builder's labels, the Windows builder's update track, build workers/memory, Prepare shaders now and FSR 4 options, and that both builders need 45 GB
  • The menu opens with Esc by default too, and with the Guide button, Back+Start or a PlayStation touchpad click on a controller
  • Cards without mesh shaders now run the compute fallback ("not supported" was out of date)
  • RX_VK_VRAM_MB's default is 45% on cards of 8.5 GB or less, 70% above
  • The game loads SDL3 first and falls back to SDL2
  • Linux can't get FSR 4 through the builder yet; it needs a source build
  • References to files and scripts that don't exist (scripts/pipeline_tour.py, third_party/PROVENANCE.md, host/nvn2.c)

Code touched

Only comments and doc paths in strings, plus tests that read docs:

  • Code comments, scripts and workflows that cited the old paths now cite the new ones.
  • The Linux udmabuf warning links the troubleshooting page.
  • test_camera_speed.py and test_stick_dead_zone.py read the settings table from docs/user/settings.md.
  • test_udmabuf_warning.py checks for the new link.

Checks

  • python3 -I tests/run_all.py on Linux: all pass (test_lift_cache.py needs the project's Python with numpy, as on main).
  • Every relative link and #anchor in the readme, CONTRIBUTING, AGENTS and docs/** resolves (script check).
  • Every menu row, default, switch, command and path in the new pages was checked against main's code.

Overlaps

Open questions

  1. Is the shader bake (BAKE.md) still used? Two tests and two source files cite it, so it's left as it is.
  2. Which environment switches should be supported for players? switches.md marks the ones that look safe.
  3. A Windows builder screenshot for install.md needs someone on Windows (there's a placeholder comment).

Commits: player docs, developer docs, the menu screenshots, then a merge of main that carries today's doc changes (the HDR tone mapper, Aspect ratio, Save my pipeline list, the -O1 development build, the presenter thread, the Windows builder runtime note) into the new pages. Review them one at a time if that's easier.

🤖 Generated with Claude Code

Joey Magnus and others added 7 commits November 11, 2026 16:48
The readme held install steps, every menu row, troubleshooting, build steps and the command
line in one 68 KB file. Players' pages now live in docs/user/:

- install.md: requirements, the Windows and Linux builders (their current labels and
  options: update track, build workers/memory, Prepare shaders now, DLSS, FSR 4), the first
  start, updating and update tracks, where the files go, uninstalling
- settings.md: every menu row on main, by tab (about 22 were missing from the readme)
- upscaling.md: which upscaler, getting DLSS and FSR 4, frame generation, Reflex/Anti-Lag 2
- controls.md, saves.md (was docs/saves.md), mods.md, troubleshooting.md
- screenshots: the Linux builder and three menu tabs (docs/images/user/)

The readme keeps the header, a short status, getting started, an index of the docs, how it
works, credits and license. Its build and command-line sections move to docs/dev/building.md
and docs/dev/running.md unchanged, to be rewritten with the developer docs.

Also: the udmabuf warning links the troubleshooting page; the two tests that read the
readme's settings table read docs/user/settings.md; links into moved sections updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ups folded in

docs/dev/ now holds one page per topic, each fact once:

- building.md: Windows and Linux setup, make_game.py and its flags, the stages, renderer-only
  rebuilds, DLSS and FSR 4 builds, the AppImage, the pipeline state list, build variables
  (the build steps were in the readme, CONTRIBUTING.md and docs/development.md)
- running.md, switches.md (every switch still read, ~170 in 13 groups, defaults checked),
  automation.md (was docs/control.md), architecture.md (design.md's architecture and replay
  rules, plus what the PR write-up notes kept repeating), testing.md, releasing.md (was
  docs/versioning.md), workflow.md (was docs/workflow.md), and an index, README.md
- cpu-profile.md, portable-builder.md, writeback-observations.md moved in as they were

CONTRIBUTING.md is cut to the PR rules and links; AGENTS.md points at docs/dev. The 33
one-topic notes in docs/ go, with their facts in architecture.md, switches.md and
building.md; design.md's dated plan and lab notes go too. Code comments, scripts, workflows
and tests that cited the old paths point at the new ones. Stale facts fixed along the way
(the VRAM budget default, SDL3-first loading, files and scripts that no longer exist).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Taken headless on a game built from main 15646d1, default settings, no mods, a Kokiri Forest
save; the build string in the corner is covered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e new pages

- settings.md: the HDR tone mapper row, Ultrawide renamed Aspect ratio (Monitor's shape by
  default), and the Debug tab's Save my pipeline list
- troubleshooting.md: sending your pipeline list; updates keep language.txt; the incomplete
  Windows Builder runtime
- install.md: language.txt is kept by an update
- building.md: the faster -O1 development build (RX_HOT_OPT) and pipeline_share.py
- switches.md: HOS_CHAIN, RX_VK_PRESENT_THREAD, RX_VK_GM_LOOP, RX_HOT_OPT,
  HOOKSHOT_LIFT_JOBS and HOS_HEADLESS as main has them; architecture.md: the presenter thread
- automation.md: option set's new rule for rows with steps of their own
- docs/windows-builder-runtime.md moved to docs/dev/
- design.md's new dated findings section (12) is left out, as its other dated sections are

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…carried in

- settings.md: the Graphics tab's new section headings and order, Field of view on the
  Gameplay tab, the Minimum preset's low-memory texture rule
- switches.md: RX_VK_PRESENT_THREAD now off by default, RX_VK_GPU_HANG_SEC, the vendor
  workaround switches, RX_VK_LOW_VRAM_MIPS; architecture.md's presenter thread likewise

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…K_FALLBACK_PHI_LIMIT in switches.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Joey-Magnus

Copy link
Copy Markdown
Contributor Author

Merged main in twice more today to carry its doc changes into the new pages: the Graphics tab's new section headings, Field of view, the presenter thread now off by default, and the new switches (RX_VK_GPU_HANG_SEC, the vendor workarounds, RX_VK_LOW_VRAM_MIPS, RX_VK_REFLEX_SUBMIT_IDS, RX_VK_FALLBACK_PHI_LIMIT).

Since this moves the readme's tables and deletes docs/development.md, every PR that edits those will conflict with it, and it with them. It gets cheaper the sooner it lands; until then I'll keep merging main and porting what changes.

The Graphics tab screenshot still shows the layout before the section headings. A retake from current main follows once the machine is free.

(_trash.panda's agent)

hckrman101 pushed a commit that referenced this pull request Nov 12, 2026
**What this is for:** stop the texture budget from charging BC1/BC4
images about twice their compressed size, and make the estimate taken
before an image is created use whole compressed blocks too, following
[hckrman101's review of
#2288](#2288 (review)).
**What is measured:** game-free runs of the production accounting with
GCC and Clang: 4,845 cases plus the original 182 helper cases pass, and
3,040 of them fail on main. One pair of game runs (main and the change,
Kokiri Forest, RTX 4090) shows no fault and no difference in behaviour,
because the budget is never reached there. **Not measured:** eviction in
play, any frame-rate effect, AMD, Windows, or a long session on a card
with little VRAM.

## Problem

The review's follow-up on BC was:

> `texel` is 1 byte for every BC format, but BC1/BC4 are 8 bytes per 4x4
block (0.5 B/texel), so they count 2x until the `x.pad` floor (which
only raises, never lowers).

`create()` summed the mip chain's texels and charged every BC format one
byte a texel. BC1 and BC4 have eight-byte blocks, the other BC formats
sixteen-byte blocks, and every level needs its extent rounded up to
whole 4x4 blocks, the 1x1 tail included. The allocator floor covers an
underestimate but cannot undo an overestimate, and the inflated count
goes into `total_bytes`, which the eviction sweep compares with the
budget.

The review also pointed at the separate estimate taken before creation
(`bytes_at`): an ASTC image the GPU samples itself was still treated as
at least one byte a texel, four times the size of 8x8 blocks and nine
times that of 12x12 blocks. That estimate decides whether the top levels
of a very large image are dropped and whether an array is made sparse.

Last, the ASTC test mixed decimal MB with binary MiB. Its example is
3,410.0 MiB by the old texel count and 53.8 MiB of blocks; comment and
output now agree.

## What changed

- `gpu/vk/vk_texture.cpp`: `texture_level_units` is used for native BC
and native ASTC in both the estimate before creation and the final
count. The helper already rounds each level to whole blocks; no
allocation and no further pass over the mip chain is added.
- ASTC that is decoded and re-encoded as BC3 counts its own 4x4, 16-byte
output blocks, not the source ASTC's block size. ASTC kept as RGBA8
still counts four-byte texels (the image's format decides, not the
source's).
- Unchanged: the allocator-size floor, a sparse image's count by what is
bound, the layer and sample factors, uncompressed and widened images,
and all of the eviction policy.
- `tests/test_native_astc_texture_count.py`: the helper's cases stay,
and the old checks of the source's text are replaced by compiling and
running the production estimate and final count: all 16 BC formats, all
14 ASTC block sizes, partial blocks, 1x1 tails, skipped top levels,
arrays, volumes and samples, decoded and re-encoded ASTC, widening,
sparse images and the allocator floor.
- `tests/test_low_vram_texture_mips.py`: its cut-out fixture now brings
the real format predicates and `texture_level_units` along, which the
changed estimate needs to compile. Every configuration and assertion is
kept.
- `docs/development.md`, the `RX_VK_VRAM_MB` row: what a texture counts,
that the log prints MiB as `MB`, and the default as the code computes it
(70% of what the driver allows the process, 45% where that is 8.5 GB or
less, at most 6 GB; on an integrated GPU less, by the system's RAM: 1 GB
with 8 GB of RAM, 2 with 12, 2.5 with 16, 3.5 with 24, 4 with 32, up to
6 above that).

The count changes; the image data, formats, uploads and compression do
not. A smaller count is not less VRAM allocated.

## How to try it

No new switch. Compare the log's `textures N MB (budget B MB)` and
`textures retired ...: budget N` lines in one scene with the same budget
settings. Game-free:

```sh
python -I -B tests/test_native_astc_texture_count.py --cxx g++
python -I -B tests/test_native_astc_texture_count.py --cxx clang++
python -I -B tests/test_native_astc_texture_count.py --cxx g++ --source /path/to/main/gpu/vk/vk_texture.cpp
```

## How it was tested

Arch Linux, Core i9-13900K, GCC 16.2.1 and Clang 23.1.3. Branch
`883c3072`, one commit on main `0ec31ff6` (rebased from `1c08cde1` on
`8568959d`: at that base main's own `vk_texture.cpp` did not compile on
Linux, `std::min` and `std::max` with mixed integer types in the
integrated-GPU budget, which is why this pull request's first AppImage
job failed; main has since fixed it).

On this head, both compilers:

```text
texture accounting: 4845 cases pass; 16 BC formats, 14 ASTC block sizes, decoded/re-encoded ASTC, uncompressed/widened, layers/depth/samples, sparse and allocation floor
native ASTC texture count: 182 cases pass (the 1365 x 1365 x 90 ASTC 8x8 array: 3410.0 MiB counted before, 53.8 MiB of blocks)
PASS 24 mip-policy configurations
```

The negative checks compile and run rather than fail on a missing
string:

| Source the test runs against | GCC | Clang |
|---|---:|---:|
| This branch | 4,845 pass | 4,845 pass |
| main (as of `de866017`) | 3,040 fail | 3,040 fail |
| The branch with only the old estimate put back | 2,508 fail | 2,508
fail |
| The branch with only the old final count put back | 1,140 fail | 1,140
fail |

The last two rows were run on the same code change one rebase earlier (a
comment and the documentation row changed since); they show that neither
half of the fix hides a missing check of the other.

The whole `tests/run_all.py` suite was run once on the earlier base
(`fce53453`): 447 passed, 3 failed, 20 skipped. The three: the
mip-policy fixture, which did not compile without the helpers and is
repaired here (above); `nx2/test_depth_sample_types.py`, whose Clang
process was killed by SIGKILL (the cause was not established; the
machine was heavily loaded) and which passes when run alone; and
`test_env_on.py`, main's `HOS_FPS_LOG` failure of that day, since fixed
by #2575.

**In the game** (RTX 4090, NVIDIA 615.78.08, headless, a save in Kokiri
Forest with camera turns, 170 s each, one after the other): main
`fce53453` and a build of the same base with this change's
`vk_texture.cpp` (the code is the same as on this branch; one comment
differs).

| Build | Run | Fatal, crash, device lost, driver lines | Textures
counted at their highest / budget | Textures retired for the budget |
|---|---|---|---|---|
| main | 170.6 s, ended by the timer | 0 | 4022 / 6144 MiB | 0 |
| this change | 170.6 s, ended by the timer | 0 | 3978 / 6144 MiB | 0 |

The pictures of both runs show the forest and the HUD as they should be.
Neither run reached its budget, so this pair shows that the change does
no harm in an ordinary scene on a large card. It does not show an
eviction that comes later, and it is not a timing.

## What to expect in play

Reasoned, not measured: with BC1/BC4 no longer counted double, the fixed
texture budget is reached later, not earlier. The allocator floor stays,
and the rules that react to the driver's own figures still use what the
driver reports, so this does not postpone every eviction. A smaller
count can also put a BC texture the GPU drew under one of the existing
keep-size thresholds; those rules are unchanged. Tiny and partial mip
levels that the old formula undercounted now count a whole block, which
the allocator floor usually covered already.

## Not tested

- Windows, AMD, the Steam Deck, or an Intel run with natively sampled
ASTC for this follow-up.
- Long play, or a card with 4 to 8 GB where the budget is reached.
- A real oversized compressed array that crosses the sparse or the
drop-top-levels threshold; the estimate itself is covered by the test.
- Frame rate, frame times or stutter.

## Risks

Unlike #2288, this changes the count on NVIDIA and AMD too. An
undercount would put eviction off too long; the per-format arithmetic in
the test, the tiny-level cases and the allocator floor guard against
that. Correcting the estimate before creation can keep a large
compressed image whole where the old estimate dropped its top levels or
made it sparse. That is intended, but how it behaves at those thresholds
needs other workloads and hardware.

## Overlaps

- #2338 and #2368 add rows next to the `RX_VK_VRAM_MB` row in
`docs/development.md`: keep their rows and this description.
- #2557 (draft) changes the set-up of
`tests/test_low_vram_texture_mips.py` beside this change's two imports:
keep both.
- #2561 moves `docs/development.md` in its rewrite of the docs: the
row's new wording should move with it.
- In `vk_texture.cpp` the overlap scan of 2026-11-11 found no conflict
with an open pull request.

No separate issue: this is the follow-up the review of #2288 asked for.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <noreply@openai.com>

Co-authored-by: GPT-6 Astra <noreply@openai.com>
hckrman101 pushed a commit that referenced this pull request Nov 12, 2026
… reach the adult's maps (#2694)

Scripts and agents that drive the game through the control bridge can
teleport, but they can't change Link's age. That leaves the adult's maps
(and checks that need them, such as GPU numbers on Death Mountain Trail
as an adult) out of reach unless there is a save of that age. The Debug
tab's Switch age already does this in the game, but only from the Insert
menu.

## What changed

- `host/control.c`: an `age` command (`to`: `"child"` or `"adult"`;
leave it out for the other age). It is asked for and taken the same way
a teleport is: at the player's next update in play, with the teleport's
tickets, result and cancel. So the replies match too: sent, with the map
it reloads; refused, with the game's reason; a timeout when not in play;
cancelled when a later teleport or age switch replaces it. If Link is
already that age, it says so and asks for nothing. Before a save is
loaded, it is unavailable.
- `host/hos_main.c`: `hos_oot_age_ask`. The player's update takes it as
the menu's age action and reports through the bridge's teleport result.
`oot_toggle_age` now says whether the map change was sent, and if not,
why.
- `host/control.h`: `HosControlGame.age`.
- `scripts/hookshot_ctl.py`: `age [child|adult] [--arrive SECS]
[--no-wait]`.
- `docs/control.md`: the command.

## Tests

- `tests/control_test.c` (the fake game): a bad argument, already that
age, the switch and its map, the age in `status`, a timeout outside
play, replaced by a later teleport, and without waiting.
- `tests/test_hookshot_ctl.py`: the client's command.
- `python -I -B tests/run_all.py` on Windows (llvm-mingw): 416 passed, 4
failed, 71 skipped. The 4 failures (`fpx_sticky`, `bake_portability`,
`input_background`, `lift_cache`) fail on `main` too on this PC (Python
modules and a compiler warning here, not this change); CI runs them.
- The first CI run failed `test_control` once: the test asked `status` 5
frames after the switch, but `status` reports the game state the bridge
samples every 100 ms. It now asks again until the new age shows (at most
2 s); 6 runs in a row pass under a full CPU load.

## Tested in the game

RTX 5080, Windows 11. A build of this branch (with #2306's commits,
which don't touch these files), headless, a child save in play on Death
Mountain Trail:

| Command | Reply | `status` after |
|---|---|---|
| `age adult` | `age switch sent: the adult, reloading
DeathMountainTrail_Adult` | age `adult`, the map
`DeathMountainTrail_Adult` once the reload is done |
| `age adult` again | `Link is the adult already` | unchanged |
| `age child` 5 s later | `refused: the game refused the age switch:
another teleport is still in progress (one every 8 s)` | unchanged |
| `age --no-wait` | `age switch to the child asked for: wait map to see
it reload` | age `child`, then the map `DeathMountainTrail_Child` |
| `age child` | `Link is the child already` | unchanged |

`status`'s `age` changes at once; its `map` follows when the reload
ends.

## Not tested yet

- Linux: wanted. On any GPU, in play, run `python -I
scripts/hookshot_ctl.py age adult`, then (8 s later) `age child`, and
post both reply lines.

## Overlaps

- #2292 (mooki000) streams the game's state, `age` included, through the
bridge, and touches the same files (`host/control.c`, `host/control.h`,
`scripts/hookshot_ctl.py`, `tests/control_test.c`, `docs/control.md`).
This PR doesn't stream anything, but whichever merges second will need a
small rebase in those files.
- #1025 (nicholasgower) can trigger Switch age from its MCP server
through the menu's cheat action. That's a different interface, not this
bridge command.
- #2561 rewrites `docs/control.md`, so this PR's section moves with it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: breakthetargets <81792782+breakthetargets@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hckrman101 pushed a commit that referenced this pull request Nov 12, 2026
**What this is for:** keep controller-button prompts working when a
RomFS mod includes resource size tables for more than one game version,
following [hckrman101's review of
#2270](#2270 (review)).
**What is measured:** on Linux, GCC and Clang both pass 12 file-routing
cases and the existing merge checks, including seven malformed-table
refusals; both new open-order cases fail against main `fce53453`. **Not
measured:** a new game run, Windows file enumeration, or any change to a
normal single-table seed's gameplay.

## Problem

The reviewer noted that:

> `build()` picks the mod table with `hos_mods_find`, which returns the
first match in hash order, while `hos_mods_romfs` passes the path
actually opened.

`host/prompts.c::sizes()` merges once and remembers the source file. If
the font is requested first, the first matching mod table can belong to
another game version: the real table is subsequently served unmerged
alongside the larger font. In the opposite order, the real table is
merged, but the font's check asks about the other table and disables the
replacement font.

The other review nit was about CI visibility, not another implementation
change. At the 2026-11-11 17:13 CST check, #2270's check list still
contained only two successful Conventional Commit title checks. That is
not evidence that its GitHub regression suite ran. The current
`.github/workflows/source-regressions.yml` already runs all discovered
checks for pull requests to main; this PR needs those checks approved
and green before merge.

## What changed

- `host/prompts.c`: discover the resource-table name in the game's own
RomFS, using the same first-by-name rule as `nx2/prompt_assets.py`,
before checking a mod's table for the font. This also works when the
font is opened first.
- `host/mods.c`: replace the prefix/suffix hash-order search with an
exact, case/slash-normalized lookup of that name. It uses the same
winning indexed entry as the normal RomFS open path.
- `tests/test_mods_size_table.py`: add a second, wholly made-up table
with different entries, exercise both table-first and font-first
requests, and check both the returned files and merged bytes. `--source
<host-directory>` runs the same fixtures against unmodified main.
- `readme.md`: explain which version's table is used.

The merge algorithm, maximum-per-entry rule, malformed-table fallback,
last-sorted-mod precedence and a mod's own font priority are unchanged.
No game file or data made from the game is added.

## How to try it

No switch or rebuild of a mod is needed. A mod containing two
differently versioned size-table files should keep its matching table's
entries and the controller font, irrespective of which file is requested
first. The game-free reproduction is:

```sh
python -I -B tests/test_mods_size_table.py --cc gcc
python -I -B tests/test_mods_size_table.py --cc clang
# In this checkout, against another checkout's unmodified host sources:
python -I -B tests/test_mods_size_table.py --cc gcc --source /path/to/main/host
```

## How it was tested

2026-11-11; Arch Linux, kernel `7.2.15-1-omarchy`, Core i9-13900K; GCC
16.2.1 and Clang 23.1.3; deps Python 3.12 with `-I -B`. Base `fce53453`,
branch `fd017d3f`. The machine was heavily loaded (load average about
287 during the focused checks). No GPU was used.
Final branch: `bd4b1ec3`, one commit on current main `ef81aee7`. The
rebase from `fce53453` was clean and its range-diff is `=`. Both
compiler proofs, including the two failing open orders without the fix,
were rerun after rebase; the relevant base host-source blobs are
identical. The full-suite totals below are the recorded `fce53453`-based
run, not a second full suite on the newer base.
The focused runs after the rebase had load snapshots of 71.63 to 75.04.

Both compilers print:

```text
ok: merge_restbl: the larger entry of each file, entries of one side kept, sorted; 7 unmergeable tables refused
ok: 12 cases served as they should: no mod, a mod with a table, two table versions in both open orders, one that grows a file without, two mods, a table that cannot be merged, no table of ours, a mod's own font, compressed and size-less frames
```

Against the base, **both compilers fail both new cases**, rather than a
source-text or compilation check:

| Request order | Main's returned font | Main's returned actual table |
Branch |
|---|---|---|---|
| Table, then font | The game's font; replacement disabled | Merged with
the correct table | Correct merged table and controller font |
| Font, then table | Controller font | The unmerged mod table; the other
version seeded the merge | Correct merged table and controller font |

The normal single-table cases still compare all merged bytes to the
independent expected table. No new game run was made: the ordinary
seed's merge and resource sizes are unchanged, and the changed
source-selection case is fully made up in the fixture. This does not
claim a new gameplay result.

The whole `tests/run_all.py` suite ran once, in the idle scheduling
class:

```text
449 passed, 1 failed, 20 skipped in 288 s (linux, 32 at once)
```

Load was 111.79 at its start and 92.91 at its end. The only failure is
the unrelated main regression `test_env_on.py`: `HOS_FPS_LOG
(host/oot_high_fps.c:15)` still uses `getenv(...) != NULL` after #2470.
That source and the guard test are unchanged here.
`test_updater_asset_digest.py` **passed** via this machine's fallback;
it is not counted as a failure. `coredumpctl` reported `No coredumps
found.`
The same guard was then run against the untouched `fce53453` checkout
with GCC and Clang; both fail on that exact `HOS_FPS_LOG` line. This is
the only remaining suite failure for B, not a mod-table regression.

Before pushing: one commit on current main, the commit identity and
trailers as the project asks, and no game file or build output among the
four changed files. Rerun on the pushed head by me: the test with gcc
and with clang passes, and against main's `host/` sources it fails with
both.

## Not tested

- Windows runtime enumeration and a Windows game build.
- A real mod containing multiple versions' tables, long play, or another
game update's layout.
- A game RomFS with ambiguous multiple applicable tables. Selection
intentionally matches the prompt-assets builder; this does not invent a
new version-selection policy.
- GitHub CI for this not-yet-opened follow-up; the local results above
do not stand in for it.

## Risks

The font's initial preparation now enumerates the game's resource
directory once per preparation rather than taking the first matching mod
entry. It relies on `hos_romfs_dir` being the game's RomFS.
Unsupported/ambiguous base layouts need a separate version-selection
decision; ordinary supported single-table layouts and the merge's bounds
are unchanged.

## Overlaps

The final standalone overlap scan found **46 open PRs** touching these
files; the actual conflict hunks were read:

- [#2151](#2151): its
punctuation helper is inserted beside `game_table`; retain both helpers.
- [#2317](#2317): the adjacent
font-output comment changes to a cache path, plus the fixture's
`string.h` include. Keep its path changes and this exact-table
selection.
- [#2478](#2478), draft:
substantive overlap. Its all-mod-table merger changes the old lookup's
fallback semantics, the README and fixture sources. The exact-key
selection must be carried into that resolver, including its
merged-table/fallback behavior; do not resolve this by blindly keeping
one side.
- [#2561](#2561): the README's
large player/developer sections are replaced by documentation links.
Move this table-selection explanation into its new mod documentation
instead of retaining the old README section wholesale.
- #2304, #2430, #2435 and #2583 touch the same code files but merged
cleanly in the scan.

No separate claim issue: this is the review follow-up to #2270.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <noreply@openai.com>

Co-authored-by: GPT-6 Astra <noreply@openai.com>
Joey-Magnus and others added 2 commits November 12, 2026 12:02
…o docs/user and docs/dev

Conflicts, and how each was resolved:

- readme.md: kept this branch's front page. Main's additions moved to the pages
  that now hold those sections: "What Hookshot connects to" to docs/user/install.md
  (linked from the readme's index); the Bug report row and its folder to
  docs/user/settings.md and troubleshooting.md; RX_VK_ASTC_BC7 to the AMD texture
  workaround and switches.md; Game language and Menu language rows to settings.md;
  Anti-Lag 2 with frame generation to settings.md and upscaling.md; the Mouse camera
  handover, the Keyboard button prompts and the five Ocarina note rows (and their ini
  keys) to settings.md and controls.md; DLSS Frame Generation's runtime to
  upscaling.md, building.md and switches.md; the Teleport minimap repair to
  settings.md's Debug section; RX_VK_WB_MAX_KB's learned write-back (with
  RX_VK_WB_PROBATION and RX_VK_LOG_WB_SKIPS) to switches.md.
- docs/development.md (deleted here): RX_VK_IMPORT_CHUNK_MB, RX_VK_NATIVE_FADD,
  RX_VK_CAPTURE_KEEP_OLD/RX_VK_CAP_BLOCKS, the new RX_VK_VRAM_MB defaults and
  RX_VK_COUNT_BY_ESTIMATE, RX_VK_NO_DRAWN_WRITEBACK to switches.md;
  HOOKSHOT_FORCE_PROMPTS and the kept button prompts of source builds to
  building.md and switches.md.
- docs/control.md (deleted here, now docs/dev/automation.md): the age command,
  its client line and quick-start example.
- docs/versioning.md (deleted here, now docs/dev/releasing.md): the nightly tag
  that only moves forward.
- docs/workflow.md (deleted here, now docs/dev/workflow.md): the two Matrix rooms
  and the agents' room in "For agents".
- AGENTS.md: kept this branch's version and added main's Team updates, Test
  requests and "a pull request is tested before it is opened" sections.
- CONTRIBUTING.md: kept this branch's version; added the test requests link, the
  in-game testing sentence of step 6, and "rebase or merge main" as main has it.
  Main's container build for the AppImage went to building.md, its DLSS Frame
  Generation lines to building.md's DLSS section.

Also: main's new docs/test-requests.md moved to docs/dev/test-requests.md with its
links fixed, and the links to it (AGENTS.md, CONTRIBUTING.md, the testing skill)
and to the moved workflow and development pages (the testing and team-updates
skills, scripts/team_chat.py) point at docs/dev. settings.md gains main's Skip
opening row (NotRexed#2827) and switches.md HOS_SKIP_OPENING. The two issue forms linked
readme anchors this branch moved; they now link install.md and
troubleshooting.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
… check reads docs/user/settings.md

Conflicts, and how each was resolved:

- readme.md: kept this branch's front page. NotRexed#2105's new option table lines
  (Ultrawide menus, Mesh shaders, Discord status, Button prompts, Compact menu key)
  were already rows in docs/user/settings.md; its Mesh shaders and Discord status
  notes went into those rows. NotRexed#2592's note on mods with tables for several game
  versions went to docs/user/mods.md, NotRexed#1630's RX_VK_PROGRAM_CACHE_ZSTD to
  docs/dev/switches.md.
- docs/development.md (deleted here): NotRexed#2825's RX_VK_MIRROR_TRIM and NotRexed#2826's
  RX_VK_EVICT_EXCESS to switches.md; NotRexed#2105's rule that a new menu row needs a line
  in the option table to CONTRIBUTING.md's code conventions, naming settings.md.

tests/test_readme_menu_rows.py (NotRexed#2105) is now tests/test_settings_menu_rows.py and
reads settings.md's per-tab tables instead of the readme's single table. Its five
checks are kept (every row has a line, every line is a row, the menu's order, four
cells with choices and a default), and it now also holds the Motion and Controls
pages' tables to their rows and order, since settings.md lists them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant