Влиты 17 коммитов апстрима после d9c64230, версия workspace 0.3.1.
База трёхстороннего слияния — d9c64230: предыдущая синхронизация
779460d8 была включена в main через squash и не сохранила родителя апстрима.
Сохранены ruRU, переключаемый интерфейс, графика MoonWell, экран входа,
музыка и задержки автовхода. Для анимации предметов принята обновлённая
проверка posing_idle из апстрима. Карта и отклонения сгенерированы заново.
Проверено на Windows: cargo check -p moonwell --locked и fmt.
Полные проверки и сборка релиза выполняются перед слиянием в main.
Синхронизация с upstream до e2aeaecf: исправления AccessKit на Windows, прокрутки трекпада на Linux и анимации моделей в меню.
MoonWell: расчёт рассеяния, вторичного освещения и AO перенесён в половинное разрешение с восстановлением по глубине. В контрольной сцене RTX 5070 суммарное время постобработки снизилось с 6,41 до 3,47 мс. При выключенном расширении изображение совпадает с исходным. Добавлен анализатор диагностических логов GPU.
Исправлена подготовка тестов интерфейса и обработка отсутствующих кинороликов в сокращённой установке. Уточнены причины существующих отклонений русского пакета и HD-атласа.
Проверки: 46 тестов графического модуля и сравнения дневных/ночных кадров пройдены; полный цикл scripts/gates.sh пройден: fmt, Clippy workspace и player, reference, тесты с установкой и без неё, документация, каталог сообщений, проверка GPU-проходов и запуск движка с данными и без. Проверки отсутствующих аддонов и четырёх кинороликов явно пропущены. Живой вход/выход на сервер не выполнялся.
A release bump changes only `[workspace.package] version` in `Cargo.toml` and the lock's entries for the workspace's own crates. The gates' memo counted only docs as unread, so the 0.3.0 bump would have rerun the whole chain on a tree whose parent had just passed it.
No gate reads the version: it reaches only the launchers' `env!("CARGO_PKG_VERSION")`, and the build-id tests pass their own strings.
`docs_only_delta` now also keeps the verdict when `Cargo.toml` and `Cargo.lock` differ in nothing but those version lines, compared with each file's workspace versions blanked. Checked on history: the 0.3.0 bump keeps the verdict; a code commit, a dependency version in the lock, a changed edition, and the bump together with its parent's code each rerun.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream 0.3.0. The ruRU pack now takes upstream's locale path
(Locale::from_language_country picks the column and the auth tag); our
own part is the Data/ruRU mount, the name GetLocale answers and the UI's
Russian name charsets. A language pack folder mounts without the base
set that upstream now requires. moonwell-gfx shadows and per-pixel
points move onto the per-vertex lit colour of upstream #2899; the
retained static pass passes its own normal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`docs/METHOD.md` asks each deviation to name the reference fact and why benilla differs, and `docs/DEVIATIONS.md` is generated from those comments. The generator wrote only a comment's lead: its first sentence, extended to at least 70 characters. A comment that gives its reason first lost the reference fact from the list, as the Warden entry did after its reason was restored.
The list now writes each comment's whole paragraph, the one the reason check already reads. The lead and sentence helpers are gone with it.
Tested by regenerating the list: every entry keeps its full paragraph, and the Warden entry shows the reference's handling of `SMSG_WARDEN_DATA` again.
Fixes#2880🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`docs/METHOD.md` asks each deviation to name the reference fact and why benilla differs. About 130 `Deviation:` comments gave no reason, and the Warden comment lost its reason when the refusal became a read-past.
- `scripts/genmap.sh --check`, now a gate, fails a `Deviation:` comment without "because" and a `Deviates` row without a Why.
- `scripts/deviation-why.allow` lists the 43 comments whose change recorded no reason. It only shrinks: an entry that gains its reason, or is new against main's copy, fails.
- About 190 comments that gave their reason in other words now say "because", with their meaning kept.
- Nine reasons, the Warden one among them, come back from the commits and pull requests that made them.
Tested by the gate on the full tree, and by its failure on a comment with no reason and on a stale entry.
Fixes#2856Fixes#2857🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Applies upstream c5fc904f..b4d0987a (34 commits): live loot, auction,
mail and trade getters; packet-ordered signals; world click records;
transport props. docs/MAP.md and DEVIATIONS.md regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`scripts/smoke.sh` and `scripts/summon-live.sh` run under `set -o pipefail` and checked their log markers as `printf '%s' "$log" | grep -q marker`. `grep -q` exits at its first match, so on a long log `printf` died of SIGPIPE, the pipeline returned 141, and a clean run failed: "no roster came on the same link after the logout", with the line right there in the log. Replaying the check on one 73 KB run log failed 15 times in 50.
Each check now reads a here-string (`grep -q marker <<<"$log"`), which no early exit can break. The same replay passes 200 of 200, and a missing marker still fails the check.
Fixes#2733
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tests that read the install assert the stock 1.12.1 data, so a
player's texture pack (Project Reforged's patch-A, -G, -T...) or an
interface mod (patch-F) in the same Data folder failed them. With
$BENILLA_STOCK_CHAIN set, Chain::open mounts a Data directory's base
set, patch.MPQ, patch-2.MPQ and speech2.MPQ, and leaves out every other
patch-?.MPQ; the language pack's folder is not filtered.
scripts/unit-tests.sh, the gates' test runner, exports it.
Verified: a chain test (stock mode keeps patch and patch-2, drops
patch-A.mpq and patch-F.MPQ; without it all mount); benilla-formats'
547 tests pass on the install with Reforged's A and G under
BENILLA_STOCK_CHAIN=1, where three character tests failed before. The
other crates' install tests were not run here. Built on Windows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A full `benilla-app --lib` run cost about 1,050 CPU-seconds, most of it building a headless client per test, and `check.sh` ran the scoped suite twice.
- `order_the_pass` sums each system's access with the host's in one reused buffer instead of cloning the host's access per system; `PassOrder` is unchanged.
- Fixtures that never run the app use `headless_client_unrun()`: every plugin and schedule but `Main` and `Update`, so the pass is never ordered. Running the app or reading the pass from it panics.
- The headless client skips the tonemapping tables, and the handler and lane access lists are measured only when a test asks.
- `check.sh` runs each scoped suite once (`test-no-install` stays in the gates) and adds the player build's schedule tests when `benilla-app` changes.
Measured: 602 CPU-seconds for the same run, 43% less.
Fixes#2324Fixes#2325🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The FPS journal writes gpu-passes.log (every top-level GPU pass) and cpu-phases.csv (wall,
process and main-thread CPU per main schedule) beside fps-journal.csv, with a session marker
per recording. On Windows the phase clocks count thread cycles (QueryThreadCycleTime /
QueryProcessCycleTime at a TSC rate calibrated against Instant), since tick-sized process
times cannot split a frame. A crate on top labels recordings through FpsJournalTags; each tag
lands in all three files when recording starts and whenever it changes, and
scripts/journal-report.py summarises a recording per session and tag state side by side.
moonwell-gfx tags recordings with its settings (mwgfx) and gets cheaper at renderScale 2:
- the ambient occlusion reads a linear depth pyramid (ao_depth.wgsl, after McGuire's SAO),
far samples off coarse levels: 2.2-2.4 ms -> ~1.1 ms at 12 MP, image unchanged (max 9/255);
- the water's scene copy is skipped while no view sees a liquid;
- shadow casters take their own visibility class, so the world camera stops listing them.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Import 48 upstream commits since 9a5f2a56 and retain the ruRU localization: Cyrillic names
keep the twelve-letter cap in upstream's reworked name check, the roster keeps its Unicode
name match beside upstream's new tests, and the spell cost cells take upstream's keyed
GlobalStrings templates, which supersede ours. The workspace stays at 0.3.0 (upstream went
back to 0.2.0 until its 0.3.0 ships); cos stays a per-platform VM parity case. Workspace
tests pass on Windows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`scripts/check.sh` ran the scoped tests with the install only, so a test that reads the install without `wow_data_or_skip!` passed every round and failed first in the gates' `test-no-install` leg, at the land.
`check.sh` now runs that leg on its own scope, right after its tests: `WOW_DATA=` set and empty, `BENILLA_REQUIRE_DATA` unset, the same `scripts/unit-tests.sh` and package flags. It builds nothing new, so it reuses the binaries the test step just built; on the three app and UI crates it ran 8434 tests in under a minute.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test gates ran `cargo test`, which runs the workspace's test binaries one after another. On a warm tree the workspace's tests take 103 s that way, and 59 s under cargo-nextest, which runs every binary's tests in one pool; all 10,411 pass both ways.
`check.sh` and `gates.sh` now run tests through `scripts/unit-tests.sh`: cargo-nextest where it is installed, else `cargo test` as before. nextest runs no doctests, so `scripts/doc-tests.sh` runs those of the library crates in scope. Each gate keeps its environment (`BENILLA_REQUIRE_DATA`, `WOW_DATA=`, the skip log), and a failing run's summary now catches nextest's `FAIL` lines. CI is unchanged.
Test: this land's own gates run under it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Integrate 182 upstream commits through b2473e60 while retaining MoonWell packaging, branding and the community ruRU pack.
Upstream replaces the C Lua/mlua host with benilla-lua/benilla-mlua and the native glue screens with stock GlueXML. The update also includes the new locale/DBC/name rules, cache and interaction handling, rendering, quest log and chat behavior, including the latest quest completion and timed-quest failure fixes. Fork adaptations use the new APIs: stock Wow.ini locales remain intact, while the ruRU overlay reads column zero and retains its ruRU authentication tag and Lua identity. Russian names keep their twelve-letter policy and Unicode roster matching; fonts, spell costs, race/class labels and translated options retain real-pack regression coverage.
Windows regression fixes keep the locale-default test valid under instrumented window overrides and the shader assertion independent of checkout line endings. The original C Lua 5.1 host, rebuilt from c0630f3d in an isolated directory, reproduces the complete Windows cos-sweep answer byte for byte; cos now uses platform-specific goldens.
Validation on Windows: scripts/gates.sh passed all automatic gates, including real-data and no-install workspace tests, reference tables, documentation, message catalog, render-pass lint, player clippy/tests and engine boots with and without data. MoonWell player clippy also passed with -D warnings. The committed tree exactly matches the full green stamp; five vendored Kira files retain the tested checkout's line endings, with normalized contents unchanged. All three actual ruRU pack regressions passed in both builds. Fifteen test archives were restored read-only from the published client and SHA-256 checked.
Each real-data/player rung skipped 31 tests because the optional addon corpus is absent (24 general, 7 Quiver). Live smoke was not run: no declared probe account; no player-account login or character creation was performed. Linux/macOS changes were not locally built.
The script host now runs on benilla-lua alone. The vendored C Lua 5.1.5 (`third_party/lua-src`) and mlua are removed, about 17k lines. `vm_parity` checks benilla-lua against the answers the C build recorded on Linux and macOS, and `front_end` checks every corpus chunk's compiled shape against the C build's digest. `GRAMMAR.md` records each grammar change from 5.1, by client address.
A handler's `this`, `event` and `arg1`… are bound in one VM call. An event fire saves and restores `event`, while the handler runner saves only `this` (`0x704d50`).
Ours on the branch: VM fixes from review (hook dispatch counts, registers and frames around a collection, protected-call handlers, library errors) and `vm_parity`'s two-VM leftovers removed.
Tests: the full gate chain on the merged tree, `vm_parity` and `front_end` among them. Live: smoke and a stock-glue login. Load time is level with C Lua, and memory is within 7%.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Sam <samuv.eth@gmail.com>
No gate built the shipped player graph outside tests. The player-tests gate compiles with `cfg(test)`, and clippy ran only on the dev feature set, so the player build's own warnings landed unseen: four unused re-exports that only dev probes used.
A new `player-clippy` gate runs `cargo clippy -p benilla -p benilla-app --no-default-features -- -D warnings` in `scripts/gates.sh`, in CI, and in `scripts/check.sh` when it reaches those crates. `docs/METHOD.md` names it.
The four re-exports are deleted: `ground_derived`, `GroundClamped`, `BodyPartsDesc` and `ATTACH_SLOT_NAMES`. Their dev users import them from the defining modules, with no `allow` left.
Test: an unused import added to `net.rs` fails the new gate.
Fixes#1465🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stock 1.12 GlueXML screens are now the default in every build: login, realm list, character select and create. benilla's native screens run only in a dev build with `WOW_GLUE_NATIVE=1`, or under the rig and capture runs, which still drive them, until the native screens are deleted. `WOW_GLUE_STOCK` is gone.
On the way:
- An unattended reconnect re-dials the kept realm and lists the roster. Under the stock screens it stalled at the realm list.
- `WOW_CHARSELECT_PICK` selects on the stock screen.
- `smoke.sh` counts the stock run's VMs and fonts.
The stock screens fix the native screens' defects listed below.
Tests cover the switch, the pick and the reconnect. Live: smoke, a kick and re-entry, the player build's boot.
Part of #856.
Fixes#849Fixes#850Fixes#834Fixes#812Fixes#360Fixes#463Fixes#79Fixes#977🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
benilla-lua is a Lua 5.1.5 interpreter in Rust that speaks the 1.12.1 client's grammar. It is opt-in behind the `benilla-vm` feature, with `benilla-mlua`, the slice of mlua that benilla-ui uses, over it. The C build stays the default. Its front end ports 5.1.5's `lparser.c` and `lcode.c`, so registers, limits and messages match the C build's.
Our finish on top:
- the collector counts growth as C does, off the write path;
- frames under a leaky generic `for` start nil, as `luaD_precall`;
- the budget hook counts what C dispatches, and costs nothing when unset;
- `__index` chains stop at 100;
- mlua 0.11's conversions;
- `vm_parity` answers recorded per platform.
Tests: `vm_parity` on both VMs; the front-end corpus (8,543 chunks) against `string.dump`; 7,613 app and UI tests on benilla-lua. Built on macOS; Linux by CI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Sam <samuv.eth@gmail.com>
With `WOW_GLUE_STOCK=1`, character select, realm list, create and the glue strings still ran native systems. They spawned hidden trees and could write the hidden `GlueDialog`. A refused scripted login showed nothing on the stock screens.
Every native glue system is now gated on `native_glue`. The cross-switch instruments (round trip, login shot, realm smoke) move into `glue_session`. Under stock, `WOW_CHAR` enters by the stock Enter World. The account guard's refusal opens the stock `OKAY` dialog, the path `CGlueMgr::Login` takes for an empty account (`0x46b090`). The schedule's ambiguity ceilings drop to 3939 and 372.
Test: a census fails on any system that touches the native screens' components or resources without the gate; it lists 44 on main. Live: native smoke, a stock login to create and back, a stock logout round trip, the stock realm smoke, and a guard refusal.
Part of #856.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`docs/DEVIATIONS.md` lists every place benilla deliberately differs from 1.12.1, generated by `scripts/genmap.sh` at every land beside `docs/MAP.md`:
- The settings that ship off the reference default, with each one's reason, from the `Deviates` rows.
- Each `Deviation:` comment's first sentence, linked to where it lives, grouped by crate and module.
A test fails if a `Deviates` row takes a shape the generator would misread. README, METHOD and CONTRIBUTING link the doc.
Fixes#1289🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The gates memo kept its verdict for a change under `docs/` or in a top-level `*.md` only, so a change to an issue or PR template ran the whole chain again. No gate reads the templates; the workflows under `.github/workflows` stay code.
Scripts only.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fresh-clone walk-through as a new contributor found four gaps in the docs:
- No quick test loop: `scripts/check.sh` has nothing to check on a clean clone, and `cargo test -p <crate>` was not mentioned. "The loop" now says both.
- The doc-link lint is a full `cargo doc` of the workspace; METHOD and the script's header now say so.
- What to cite while a benilla-re gap is still open: the client address, with the benilla-re issue or pull request linked.
- When `reference/PIN` moves: only with a change that needs a newer benilla-re row, to a commit on its main. Stated in CONTRIBUTING and `reference/README.md`.
Docs only.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`reference/` held benilla's own copies of the reference's tables, some derived by its own scripts from older inputs: the fire-site table kept one event id where a site fires two, and credited glue-screen sites to in-game events.
Now `reference/` is benilla-re's tables at the commit `reference/PIN` names, byte for byte: bindings, events, glue events, messages, event arguments, verb events, the glue surface, the Lua globals, chat types and groups, and CVars. `scripts/sync-reference.py` copies them, and its `--check` runs in the gates and in CI. Four generators and nine old files go, and every reader moves to the new columns.
New tests hold the chat type, chat group and CVar tables to the copies; the CVar rows already filed are listed as known.
Fixes#245Fixes#960
Part of #244🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A failing test gate printed only its log's last 30 lines. Those are the doc-test runs after the failure, so the failing test's name and its panic were missing from both the gate output and the land log.
scripts/gates.sh and scripts/check.sh now print the panic lines, each test's FAILED line and any compiler errors from the log first, then the tail as before.
Checked on a sample failing log; both scripts pass `bash -n`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Verb events fired from app feeds a frame late; 1.12.1 signals inside the call.
Now each verb raises its events in place. Feeds push only what their own server source changed, so a verb's write is never rolled back.
Also, from the bytes:
- The score board waits on pending names (0x4aa216).
- The recruitment cascade runs inside its verb.
- Seven mail verbs share the request lock 0xb6efc8; the mailbox keeps its inbox across a close.
- StartAuction runs its gates, sends the attached guid and empties the slot in the call.
- The tradeskill repeat counts, disarms and resets as 0x4fd7b0 and 0x4fc750 do.
- Quest folds carry across a /reload.
Tests cover each event's timing and the feed rule.
Fixes#1008Fixes#1009Fixes#1010Fixes#1045Fixes#1046Fixes#1047Fixes#1050Fixes#1051Fixes#1059🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 1.12.1, logging out or a refused character login returns to character select on the same world connection and re-lists there (`0x5aaeb0` → `0x401ee0`, then `0x46b880` → `CMSG_CHAR_ENUM`). There's no realmd logon and no new `CMSG_AUTH_SESSION`. Enter World closes the realmd link (`0x46b70c` → `0x5b3320`), and only the glue's own re-dials reopen it.
- benilla used to drop the socket and log on again from cached credentials. Now the world session splits and rejoins on one connection, and the header cipher carries its state across.
- The realmd link closes at the pick.
- A leftover packet at character select is skipped rather than fatal.
- The write thread applies a pending writer swap before any command.
- A kept-link teardown ends the character without a "server down".
Fake-server tests pin one logon, one auth and one more enumerate. The logout smoke fails without a same-link relist.
Fixes#984Fixes#983🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first piece of running the stock GlueXML: benilla had no glue Lua state, and no reference data for its surface.
In 1.12.1 the glue state holds the base set (`0x7039e0`) and seven registrars (`0x46abb0`): 119 functions, 105 of them glue-only, with 26 glue events (`0xb41e70`, `0x46a88f`). `RegisterEvent` matches names case-insensitively and drops an unknown one (`0x702140`). The glue builds the 13 base frame types plus `ModelFFX` (`0x46a4fe`), a Model with its own show and paint (`0x46fa00`); the in-game types come only at world entry (`0x495940`).
`reference/` gains the glue event and surface tables, generated and checked against the shapes table. `UiScript::new_glue()` builds that state; a gate holds its globals to the table, with the 108 unbuilt names listed. Nothing in the app changes yet.
Part of #856🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Integrate six upstream commits through c0630f3d (2026-10-11), preserving MoonWell branding, launcher integration and ruRU localization.
The update fixes OnUpdate ordering by strata/level/link order, bare Lua errors passed to the error handler, case-insensitive addon file lookup and UNIT_FACTION on faction-template changes. It also adds an optional Bevy dynamic-linking feature and lets test gates report all failing crates.
Merge conflicts retain localized spell costs, race/class display names, Cyrillic character-name input and the twelve-letter creation limit. UI error handling and update traversal follow upstream. docs/MAP.md was regenerated and has no delta.
Validation: full scripts/gates.sh passed on the exact source tree (1884 s) on Windows with the real 1.12.1 MPQ/DBC data and ruRU pack, including workspace tests with and without install data, player tests without dev, and engine boot checks. The 31 optional addon corpus tests are unavailable on this checkout. No live account login was attempted because no probe identity is configured. The optional dynamic-linking feature is retained but not enabled by the default gates.
`cargo test` stops at the first test binary that fails, so a red `scripts/check.sh` or gate run showed one crate's failures and hid the rest: the next run found more. Both scripts now pass `--no-fail-fast`, so one run reports every failing test across the workspace. The exit status is unchanged: any failure still fails the step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Integrate the MoonWell launcher and deployment tooling on the local upstream baseline d0d1b5de. Correct the shipped icon path, regenerate the code map, and make source/Lua gates portable to Windows. Classify launcher startup icon access as input plumbing.
Verified on Windows: scripts/gates.sh ALL GATES GREEN; MoonWell release build without dev; 12 deployment tests; deployment and packaging script syntax. Stock MPQ chain excludes the custom patch-3. Thirty-one addon tests skip per data-enabled rung because the corpus is absent. Native macOS/Linux and live login smoke were not run; no declared test account is available.
The land chain compiled the player build twice: `cargo build -p benilla --no-default-features`,
then `cargo test -p benilla-formats -p benilla-app --no-default-features --lib`. The test build
already compiles every line of the no-`dev` graph but the `benilla` launcher, a 17-line shim with
no `dev` seam, and `benilla-app` has no `cfg(not(test))` code, so the first step added only the
shim and a link: about two of a chain's twelve minutes. It goes from `gates.sh` and CI, and
`player-tests` carries its comment.
`check.sh` no longer says a full chain before land makes the land free: main moves in between, so
the chain ran twice.
Test: this land's own chain runs the new `gates.sh`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The core now loads the chain's `Interface\FrameXML\Bindings.xml` after the `FrameXML.toc` rows, as the reference's UI load does. The load checks the file at `0x48fff9` and loads it through the addons' loader `0x4b6f70` at `0x490018`. The default keys come from the chain's `WTF\DefaultBindings.wtf` (`0x4b62b0`). Every command now runs the stock file's own body, and the registry compiled into benilla, with its 91 retyped bodies, is deleted.
- **The loader's rules (`0x4b6f70`).** It reads the root's children only and skips empty, `debug="true"` and non-Windows rows. A second definition of a name is skipped, and duplicate headers are refused.
- **Keys and commands are separate tables, as in 1.12.** `SetBinding` never reads the command (`0x4b7490`). `RunBinding` runs the body in the VM. `RunCommand` sets `keystate` around the body and clears it after (`0x4b7c42`).
- **Engine behaviour sits behind the Lua globals the bodies call.** These are the movement, turn and mouse-steer bits. Focus loss clears the direction bits and keeps the mouse pair (`0x514490`). One key's stop clears its bit, as `0x514b70` does, so releasing W while UP is held stops the character.
- **Behaviour changes that follow the stock bodies.**
- The nameplate keys (V, Shift-V, Ctrl-V) now run the stock bodies.
- ALT-Z runs `CloseAllWindows(); UIParent:Hide()`.
- The minimap, sound, stance and bonus-bar keys call their stock functions.
- The wheel zooms one `CameraZoomIn(1.0)` per notch.
- **Platform.** benilla answers as the PC build, so a `platform="mac"` row does not register on any OS.
Verified:
- New tests: a fixture `Bindings.xml` (reordered, edited, and with debug, empty, nested and Mac rows) drives the command list, defaults and bodies. The V keys follow the stock bodies. No stock body's text remains in the tree. Every stock body's calls resolve after a production load. Each test fails with its change undone.
- `scripts/gates.sh` and `scripts/smoke.sh` are green.
- A live run with `WOW_GM=off`:
- W and Space moved and jumped.
- B toggled the backpack.
- 1 reached `UseAction`.
- V, Shift-V and Ctrl-V behaved as the stock bodies do.
Built on macOS; no platform seam touched.
Fixes#570Fixes#563🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The host's state leaves `_G`: the eleven `__benilla_*` globals, which any addon could read and overwrite, move into the host's model (and, for the error handler, the Lua registry, as the reference keeps it), and the getters over them are Rust bindings answering the reference's shapes.
- `GetTime` returns one number (`0x515ea0`), and `GetGameTime` returns hour then minute (`0x515ee0`). The tick advances the model clock, and every cooldown and buff reader reads it.
- `GetZoneText`, `GetRealZoneText`, `GetSubZoneText` and `GetMinimapZoneText` always answer a string, `""` when unset (`0x48a0a0`-`0x48a100`). `GetZonePVPInfo` answers three values (`0x48d540`).
- `seterrorhandler`/`geterrorhandler` work over a registry slot that starts empty, as the reference's does (`0x7039e0`, `0x702950`, `0x702900`). A non-function raises `Usage: seterrorhandler(errfunc)`. The engine dispatches caught errors through the slot, not through the global (`0x703b40`). The stock `BasicControls.xml` installs `_ERRORMESSAGE` before any addon, so play is unchanged.
- The engine's surface allowlist now holds only `_G`. `scripts/api-coverage.sh` no longer excuses `__` names.
Verified:
- New tests show each of the eleven names absent from `_G` and that writing it changes nothing. On main, `__benilla_now = 0` rewound `GetTime` and every buff and cooldown.
- The getter shapes and the error-handler slot are pinned. The surface gate fails when a name is re-added.
- `scripts/gates.sh` and `scripts/smoke.sh` are green.
- A live run read the clock, zone texts, PvP info and game time, then showed that writing the old names changes nothing.
Built on macOS; no platform seam touched.
Fixes#576🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The core now loads the player's own `Interface\FrameXML\FrameXML.toc` off their chain, row by row in its order (`0x6edb90`, from `UI_Init` at `0x48ffed`), instead of benilla's copy of the list, and `benilla.toc` is gone. Order is observable in 1.12.1: event listeners run in registration order, children are kept in creation order, and hit-test ties go to the earlier frame.
- The world VM starts bare: `GlobalStrings.lua` and `Fonts.xml` load as the toc's first rows. The boot VM keeps `GlobalStrings.lua` and the emote tokens, and no longer loads `Fonts.xml`, which nothing reads there (#307).
- A chain without `FrameXML.toc` builds no stock UI and still loads the layer and addons, as the reference does (`"Couldn't open %s"`, severity 2, return unread); benilla reports it as an error.
- Every test kit now loads in the production order, `FrameXML.toc`'s rows then `layer.toc`'s, and a guard fails any kit that loads out of it. Two tests whose premises the stock order disproves are gone.
- `scripts/smoke.sh` counts VMs by the VM marker, since `Fonts.xml` now loads once per world VM.
Verified: new tests for the load list against the chain toc and a reordered fixture toc, the missing-toc case, and two stock frames on one event running in the toc's order (ExhaustionTick before PlayerFrame on `PLAYER_UPDATE_RESTING`), each failing on main's order. A before/after dump of globals, frames, draw order, event listeners and saved variables differs only in stock-order effects: the reordered listeners and children, draw-order ties between non-overlapping frames, and `UnitPopup`'s quality colours now sharing the live tables. `scripts/gates.sh` and `scripts/smoke.sh` are green, and live before/after runs show 0 errors and identical warnings. Built on macOS; no platform seam touched.
Fixes#555Fixes#307🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
benilla's own interface now loads as one layer on top of the stock UI, as `docs/METHOD.md` now states: the core loads the stock FrameXML rows, the layer (`assets/ui/layer.toc`) loads after the last of them and before the third-party addons, and like FrameXML it has no AddOn API row and fires no `ADDON_LOADED` (the reference loads `FrameXML.toc` outside `AddOn_Load`, `UI_Init` `0x48fbf0`).
- Moved into the layer unchanged: `ScrollTemplates.xml`, `KeyBindingsPage.xml`, `OptionsFrame.xml`, `ContainerFrameAdapters.xml`, `SpellBookAdapters.xml`, `ScriptLogFrame.xml`. `GameMenuFrame.xml` stays the core's last row until #553.
- The host's Lua fixes to stock code (`FCF_ChatTabFadeFinished`, the `DurabilityFrame` re-seat) move into the layer's `FrameXMLFixes.xml`; the resize re-seat stays host-side as an engine adaptation.
- The core now loads `StatsFrame.xml` and runs `LocaleProperties.lua` once, from `UIParent.xml`, so its chain rows are exactly the stock `FrameXML.toc` rows but the menu; the order is #555.
- A dev build boots without the layer under `WOW_STOCK_UI=1`; the host's calls into the layer are guarded.
Verified: new tests for the layer's file list, the core list against the stock toc, the load position (a `_G` definition log), absence from the AddOn API and the stock-only boot, each failing with the change undone; a before/after dump of every global, frame, draw order and saved variable differs only by `StatsFrame` and the moved fixes' source; `scripts/gates.sh` and `scripts/smoke.sh` green, and a live logout/re-login under `WOW_STOCK_UI=1` clean. Built on macOS; no platform seam touched.
Part of #552Fixes#542Fixes#543🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The `player-tests` gate now runs its 30 addon-corpus tests where the corpus is: it passes the `wow-addons-vanilla` link as `$BENILLA_ADDON_CORPUS`, the only way a build without `dev` finds the corpus, and sets `BENILLA_REQUIRE_DATA=1` under the same condition as `test`, so a skip there fails the gate as `docs/METHOD.md` asks. All 30 pass in the player configuration; CI, which has no corpus, is unchanged.
Fixes#453
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The gates of scripts/gates.sh that need neither the install nor a display (fmt, clippy, the
workspace tests without an install, the doc-link and render-pass lints, the player build and its
tests), on ubuntu-latest, so a red pull request is seen before anyone builds it. A branch of this
repository skips the job: it runs the full chain before it lands. The job reads the repository
only, keeps no credentials in the checkout, pins its two actions by commit, drops debuginfo for
the runner's disk and caches dependencies. gates.sh and CONTRIBUTING say what runs where.
actionlint passes; the system packages are the ones CONTRIBUTING names for Linux. The workflow's
first run is a manual one on main once it lands.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The comment and string passes' last leftovers, and one lookup made to match the reference.
**Comments that stated a wrong reference fact**, now stating the right one:
- A cast failure clears the GCD, and a failed cooldown-on-event spell also drops its parked record (`0x6e73cc`).
- `SetUnit` is `0x505d70`; `0x476cb0` is the component clone it reaches.
- Interior sound follows the character, where the listener sits, not the camera.
- Template inheritance re-runs `LoadXML` on the one object against each template (`0x76985c`), and reads `name` and `id` from the instance alone (`0x769770`).
- On the ghost-to-alive edge the reference clears its corpse cache and sends nothing (`0x5ee990`, `0x491f50`).
- The name cache has no high-bit miss. An empty-name answer evicts the record in the reference (`0x55f6f0`).
- The combat-log deferred queue (`0xc4e208`) waits only on an item name. A unit name never waits.
- A flying spline's segments are paced by curve length (`0x454320`, `0x453760`), not by chord length.
- A stop within 3 yd of its stop point keeps the arrow nocked (`0x6019e4`).
- A missile's sound id is at `CMissile+0x40`.
- A nil or unresolved `AssistUnit` token is a gap against `0xb8` ERR_GENERIC_NO_TARGET, not a deviation.
**Strings:**
- Test, log and tool messages lose leftover history wording and placeholder labels.
- The extractor's scan summaries describe the current tint and sequence-clock paths.
- The smoke script's failure text carries no private numbers.
**Line continuations:** twelve log, panic and assert strings get back the `\` continuations they had lost. Each printed a run of spaces mid-message. Fixes#106.
**One lookup:** a region's `inherits=` is looked up as one verbatim name, matched case-insensitively (`0x6ee6f0`, `_strnicmp` at `0x6ee747`), as `framexml::expand` already did for frames. The gate before it split on commas and matched case-sensitively, so a texture or font string whose `inherits=` spelled its template in another case never received the template. `a_region_takes_a_mis_cased_template` fails before this change and passes after.
**Tests:** the quest tests stop loading three FrameXML files that no quest file uses.
**In game:** nothing changes for the stock UI. Log lines read as one sentence, and an addon texture that names its template in another case now gets it.
Verified with `scripts/check.sh` on the changed crates, plus the loader and quest tests with the install present.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Two small fixes.
- `OptionsFrame.xml`: the comment on the missing Use UI Scale checkbox said benilla has no `useUiScale`. It is registered at "0" and never read, which is why the UI scale always applies.
- `scripts/doc-links.py`: `--help` and `-h` print the usage in the module doc instead of running the full `cargo doc` pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comments that pointed into the maintainer's private reverse-engineering notes (a note by name, a
path, a section or finding id, the analysis's own name for a client function, a process word) now
carry the fact itself, cited by the reference client's address, by vmangos file and line, or as
"the reference". Nobody outside could follow those pointers.
Comment text only, plus the test-assert and log strings that carried the same pointers. No claim
changes: a comment the notes have since corrected keeps its wording here, and correcting it is
separate work.
The two reference generators, `scripts/gen-reference-globals.py` and
`scripts/gen-reference-verb-events.py`, named the private notes' layout to find their inputs. They
now take the input files as arguments (`--capture`, `--framexml`, `--names`, `--extents`,
`--disasm`). Run on the same inputs, both write the committed tables row for row; only the two
header lines that named the old environment variable change.
Checked per file:
- the code is identical: each file with its comments stripped compares equal before and after; a
changed string literal is compared under a mask that proves only literal text moved and every
literal kept its line count;
- no client address the old text cited was dropped;
- a second reviewer checked every added address against its derivation;
- a detector for the notes' names, paths, ids, function names and process words finds none left;
- `scripts/gates.sh` is green.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There is no top-level assets/ directory: assets/ui lives under
crates/benilla-app/, which the crates arm already scopes to benilla-app.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The test gate refuses skips only where both the install and the
wow-addons-vanilla link are present. The link is gitignored, so the
tree hash cannot see it, and the stamp kept only the install path. A
green earned without the corpus was therefore reused after the link
appeared, and skips were never refused on that tree. The corpus's
presence now sits beside the install in the stamp. The memo message
also names docs/ as well as top-level *.md.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each rung keeps one or two lines: what it refuses and the fact that
makes it necessary. The stories of how each gate came to exist are
gone. 148 comment lines become 48; the code is unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A failure message pointed at records and bug ids that are not in this
repo; each now says the fact alone. Only message text changes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Docstrings keep their usage and exit codes; table headers keep the
column contract and the client addresses and lose the dates, the
correction history and every path into another repository. A
generated header changes in its generator's template, so each of the
four generators still reproduces its output byte for byte, and no data
row changed in any of the seven tables.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With no `.probe-identity`, the scrub kept the shell's WOW_USER, WOW_PASS
and WOW_CHAR in the environment, so the realm leg inherited WOW_CHAR and
`realm_select::smoke` refused it: the documented
`WOW_USER=… WOW_PASS=… WOW_CHAR=… scripts/smoke.sh` could never pass.
The account is resolved into the script's own variables first and each
leg passes it by name, so it now leaves the environment either way.
Verified: the scrub run alone with the account in the shell leaves only
WOW_HOST in a child's environment, with and without a declared identity.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each header is a one-line summary, the environment it reads and its
usage; the history, measurements and record numbers go. Only comment
lines change: with comments stripped the scripts are byte-identical.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
465 tests loaded the stock interface off the install without declaring
it, and 25 addon tests assumed the install beside the corpus; each now
opens with `wow_data_or_skip!`. The gates run the workspace tests a
second time with `WOW_DATA=`, so a test that reads the install
undeclared fails at land instead of on a contributor's clone. The skip
log writes each line in one call so parallel tests do not interleave.
Verified: the workspace tests with `WOW_DATA=` in a checkout that has
the addon corpus pass 6972, fail 0, and log 1555 skips; on a fresh
clone of main before this change they failed 465.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>