`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>
A cast's push and arm signals shared one park, so the arm signal's listeners never saw targeting up. A TargetUnit or a cast there acted as if no cursor stood. 1.12.1 writes the targeting word between the two signals (`0x6e525e`), so only the arm's listeners see it.
**Changed**
- The attempt parks after its push (`0x6e5026`) and again after its arm (`0x6e5333`).
- The targeting word is its own state, apart from the cursor mode. A send lowers the mode (`0x6e5547`), but the long-range send leaves the word standing (`0x6e5454`-`0x6e5467`).
- The ground decal follows the word (`0x4820f9`, `0x48383b`).
- A cursor re-armed at the arm signal keeps its cast in flight.
**Tests:** a TargetUnit, a cast and a cancel at the arm signal; SpellIsTargeting() after a far send; the decal. Each fails on main.
Part of #2271.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A display swap despawned a unit's spell effects with its old visual while their instances still named them, so a later teardown despawned a dead entity. 1.12.1's rebuild ends every effect on the unit first (`0x60abe0`, `0x60ac10`), then replays the auras' and the channel's kits.
**Changed**
- A rebuild runs only when `0x60ae10` says the display changed. It ends the effects started before it on the wire, and replays the auras' and the channel's kits on the new body.
- Kit sounds fade over 0.15 s as they stop (`0x6204b6`), and each loop is tracked per kit.
- benilla's rig heal keeps what the unit had.
**Tests:** a swap and a heal with effects up, the wire order within a frame, the gate, the replays, the fade. The swap tests fail on main.
Fixes#2843
Found in Adrinalin4ik/manila@cd8790bc🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since #2835, the lanes fire after each packet's handlers while the in-game interface is up, which is every packet in play. 162 tests in 30 files dispatched packets with the interface down, so they exercised the frame-end path, which ships only for the loading screen.
**Changed**
- Headless test clients boot in the world with the interface up. The lanes and chat harnesses do too.
- Tests read a packet's lane items at its end, through a tap, not after the frame's drain.
- Four expectations now hold the in-play order. For example, `SetPlayerControl` resets the cursor before PLAYER_CONTROL_LOST (`0x495901`, `0x49590b`).
- Tests of the session end, the hello and the world entry stay out of the world on purpose.
**Tests:** the suite passes. No product code changed, and the move found no product defect.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cast fixed its target before the push and arm listeners ran, so a listener that retargeted, raised a cursor or refilled the record could not change what was sent. 1.12.1 binds after them (`0x6e5338`).
**Changed**
- Every attempt parks at `0x6e5338`. On resume, the bind leg reads the word, the cursor and the record again (`0x6e5359`-`0x6e5477`), then TryCast's tail.
- An item use keeps the selection it passed (`0x4fa163`); a press re-reads it (`0x6e539f`).
- An out-of-range unit falls through to autoSelfCast (`0x6e53d7`).
- "You have no target" or "Invalid target" follows the selection (`0x6e5084`).
- Feed Pet's arm listeners run before the food is bound.
**Tests:** a retarget, a listener's cursor, a refill, an item's fixed target, the self-cast fall-through, Feed Pet's cursor. Each fails on main.
Part of #2271.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A loot close a packet owed was made at the frame's end (`drain_loot`), so a listener between two packets saw the window open, and the release went out late. 1.12.1 closes it inside the packet, at `EndLootWindow` (`0x48f200`).
**Changed**
- A precast end, a GO, a cast result, a move start and the server's stand state each close the window inside their packet. Each release goes ahead of the handler's later sends.
- Every close cancels the rows' pending record fetches (`0x4c1cb0`). A move or a sit closes only while a loot target is latched (`0x6126b0`).
- A chest's GO arms its loot target after the close (`0x6e831b`).
- `drain_loot` is gone. With it, the last frame-end write a listener could read is placed.
**Tests:** each close inside its packet, the release order, the latch gate, the fetch cancel. Each fails on main.
Fixes#2217🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A remote unit's move was seated by the frame's movement pass, so a listener between two packets read where the unit stood before. 1.12.1 seats a move inside its packet when the movement clock has it due (`0x618dd2`), and holds it for the tick when not.
**Changed**
- A due move seats in its handler, stepped on by its lateness (`0x618bb0`). A rider seats on any game object flagged as a transport (`0x630a80`).
- A create seeds the unit's relay chain with its stamp (`0x619320`). On vmangos, a player who stood still before coming into view now trails by up to a second, as in 1.12.1.
- A plain relay ends its packet; only the speed relays read on (`0x6010e0`).
- `GetBattlefieldPosition` and `SpellCanTargetUnit` read positions at the call.
**Tests:** due and held moves, the catch-up, riders, the create seed, each read between packets. Each fails on main.
Part of #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cast's push and bind signals, the selection's tutorial ladder and five smaller stretches still ran their listeners after the sends behind them. 1.12.1 runs each listener at its signal, before the verb goes on (`0x703e50`).
**Changed**
- A cast stops at `SendCast`'s entry (`0x6e54f0`), runs its listeners, then reads the word and the record again. A cancel there leaves the cast unsent (`0x6e5580`), and the swing waits for the sent spell (`0x6e51be`).
- The selection's ladder signals before its send (`0x49388d`).
- StartAttack's refused stop, the click's quest-giver tutorial, the soulstone's cancel, and the stand change's loot close each signal before the sends after them.
- The send census's allowlist is empty.
**Tests:** each signal's listener sends ahead of the verb's send, and a cancel at the push signal. Each fails on main.
Part of #2271.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item records were fed to the interface at the frame's end, and their answers ran there too. So a listener between two packets read a record as it stood before. 1.12.1 asks inside the call and runs each answer's callbacks in their list.
**Changed**
- A getter's miss asks inside its call (`0x55ba30`), and a new interface's copy is seated at its load.
- The answer callbacks run in registration order (`0x55bf12`), for the try-on, the auction lists and the combat log. Combat lines on one item are written one per answer, newest first (`0x6295b0`), through their formatter's gates again. An enchantment line asks for its item before its gates (`0x628f83`).
- An item text read and the mailbox open inside the click or the call (`0x4acd42`).
**Tests:** each read between two packets, the callback order, the combat-line order, the mailbox's MAIL_SHOW. Each fails on main.
Part of #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nameplates were driven from the frame's end, so a listener between two packets saw a plate as it stood before them. 1.12.1 moves each plate at its unit's field callbacks, inside the packet.
**Changed**
- A plate's bar, level and colour change at their unit's callbacks. The bar reads health signed (`0x7cc41b`) and the level drunkenness lowers (`0x7cbce0`).
- A unit's watchers run in link order (`0x467f00`): its event bridge, its plate bars, the tooltip's bar. The walk reads the live list (`0x4655df`).
- The token flags follow their writers in place: selection, party, raid, create, pet, return. They survive a reload, as they do on the unit (`+0xc90`).
- A tooltip links a watcher only on a unit in view (`0x467e86`).
**Tests:** each read between two packets, the watch order across a reload, the party recolour. Each fails on main.
Part of #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
More calls and turns held a signal past a later effect of the same apply. 1.12.1's `SignalEvent` (`0x703e50`) runs every listener before the verb goes on.
**Changed**
- Each queued trade half signals before the next one sends (`0x4bfa70` per item).
- A bind answer frees its record after its listeners (`0x5e1d09`).
- StopAttack runs as head, send and save with points between (`0x5ecb17`, `0x5ecb5b`, `0x6e6ff3`), and a call's tutorials fire at its points.
- A move's cast cancel signals before its channel cancel and the tick (`0x51512a`, `0x515193`, `0x5151a0`).
- SendChatMessage's AFK and DND lines signal before the packets after them (`0x5eb801`, `0x49f5fc`).
- A pick made at a point signals its target change once.
**Tests:** the wire order for each, the census over queued reports and tutorials, one target change per pick. Each fails on main.
Part of #2271.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Fixes#2838Fixes#392
The world map, its landmarks, the world-state readout, the recruitment cascade and the channel walk were fed at the frame's end. 1.12.1 reads each at its call or runs it at its packet.
**Changed**
- `GetCorpseMapPosition` and the map setter read the corpse, the explored bytes and the landmarks as they run (`0x492090`, `0x4a6bf4`, `0x4a6819`).
- The readout's rows are read at the call (`0x4c5a70`), and its flip signals inside its notice (`0x49bdd6`).
- The recruitment cascade (`0x49ea90`) runs at each caller, the chat cache's AUTO line included (`0x498e35`). The taxi flag's walk runs at its watcher (`0x5ff56e`).
- A corpse answer is kept only for a ghost (`0x48f766`). A ghost-bit edge resets it in its packet and asks only for a ghost (`0x491f50`).
**Tests:** each read between two packets, the readout's signal, the walk, the cascade, the corpse on both edges. Each fails on main.
Part of #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Fixes#2836
`LoggingChat` and `LoggingCombat` answered their flag before the app tried the open on a later frame, so a failed open still answered 1. 1.12.1's verb (`0x49fe50`) opens the log inside the call (`0x65a0c0` at `0x49fea2`), clears the flag when the open fails (`0x49feab`), and only then answers it.
**Changed**
- The verb's open runs through the call host, and a failed open clears the flag before the answer.
- Writes gate on the flag (`0x49acde`, `0x49b1d3`). Turning it off keeps the file open, and the chat teardown closes both (`0x498700`).
**Tests:** an unwritable path answers nil for both verbs. A writable one answers 1, writes the same chunk's line, stops on off, appends on on, and closes at the teardown. Each fails on main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Several calls and engine turns held their signals past later effects of the same apply. 1.12.1's `SignalEvent` (`0x703e50`) runs every listener before the verb goes on.
**Changed**
- A selection teardown's strike cancel (`0x493a08`) signals before the store, the send, the usable refresh and the pet stop (`0x4bd650`).
- SetCommands' tick reports its cancel at `0x51512a`, the AFK line before its send (`0x5eb878`), and freelook at `0x51491f`.
- AUTOFOLLOW_END comes before the follow's stores (`0x61113e`), mail's signal before its arm (`0x4add9d`), a right-click's ClearCursor before the interact, and a refused bind's signal before the deselect (`0x6e615d`).
**Tests:** the wire and census order for each, and a strike-cancel listener's pet stop signalling once. Each fails on main.
Part of #2271.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The item rows read the frame's final state: locks were forgotten as the frame left the index, tooltip records and the bind point were fed after the packets, and equip callbacks were replayed at the frame.
**Changed**
- A lock lasts until its item is freed (destroy `0x4674a0`, reuse `0x466e55`, teardown `0x467800`). A release keeps it.
- An item's deactivate (`0x5d8660`, from the release at `0x46496f`) lets go of its loot, cast, cursor and reader, and cancels its record wait. A brought-back item sets up again (`0x5d8430`).
- A tooltip build and `GetBindLocation` read at the call (`0x5075f0`, `0x48dae3`). A cached equip record acts inside the call (`0x5e163b`).
**Tests:** each lock path, each deactivate leg, a bring-back's set-up, a tooltip between two writes, the equip send in the call. Each fails on main.
Part of #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Fixes#2833Fixes#2834
The bindings, layout and macro readers reran at the frame's end off the sections a download stored. 1.12.1 runs each inside the settle its digest or download handler makes (`0x5afaca`, `0x5afd19`): the bindings `0x4b6240`, the layout `0x490380`, the macros `0x4f0600`.
**Changed**
- Each reader runs at its section's settle, ahead of the next packet.
- The interface load registers the readers after VARIABLES_LOADED (`0x4900c2`-`0x4900d1`), so a `/reload` reads the bindings and signals UPDATE_BINDINGS there. File scope sees the fresh, empty binding table.
- A 0.2 macro file's button moves wait for the bar's arrival (`0x5deae2`). The file is rewritten only after them.
- A save queued before the set choice is dropped (`0x4b6506`).
**Tests:** a download between two packets for each reader, the `/reload` order, the macro moves with the bar before and after the settle, a save at file scope. Each fails on main.
Part of #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes#2202Fixes#2831
A nameplate's click was queued and applied by frame systems after the click returned. 1.12.1's click slot `0x7cb910` runs `0x4949f0` at `0x7cb92a`, ahead of `RunClick` (`0x7cb936`): the left leg's trade, pet feed, greeting and `SelectTarget` (`0x4925d0`), or the right leg's select and interact (`0x492820`).
**Changed**
- Both legs run inside the click, for a press and a scripted `Click()`, on buttons 1 and 4 only, a disabled plate included.
- A select's re-swing is judged on the unit (`0x5ecb70`: alive, `CanAttack`), not the cursor, at every select.
- A retired plate keeps its guid (`0x608a10` never writes `+0x4e8`), so its click still selects that unit.
**Tests:** `Click()` then a same-chunk target read, the select's events ahead of `OnClick`, the trade, feed and bind order, the re-swing under any cursor, a retired plate's click. Each fails on main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Fixes#2168
Listeners fired once a frame, at a drain after every packet, so one packet's events could fire after a later packet's writes. 1.12.1 runs each packet's handler whole, in wire order (`0x5384d0`, `0x537ae5`), and a signal runs every listener before it returns (`0x703e50`).
**Changed**
- The lanes flush after each packet's handlers, and before the first packet for what input and the tick stamped.
- An update packet's blocks, a speed packet's pose and a saved answer's bytes stay inside their packet.
- Log starts become span starts. Chat's age pass runs once a frame.
- The guard holds each lane's own reads.
**Tests:** a listener's send ahead of the next packet's query, per-log reads between packets, close shapes across lanes. Each fails with one drain. Twelve suites now run in the world.
Part of #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The guard listed `sync_cvars` and `sync_input_cvars` as frame-final reads: a handler's CVar write was said to reach the VM's mirror only at the next sync. No handler writes the registry but through the call host, whose appliers sync the mirror inside the call, as `CVar::Set` runs its callback inside its writer (`0x63df7d`) and `GetCVar` reads that record (`0x488bf9`). Every other writer runs before the input sync or after the dispatch.
**Changed:** both rows move to the no-view list with that reason. The frame-final counts fall to 12 rows and 36 inputs.
**Test:** `every_cvar_write_reaches_the_mirror_before_a_packet` fails when a handler holds the registry past the host, or a writer runs between the input sync and the packets. A registry write in the weather handler fails it.
Part of #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes#2822
**Wrong.** `lanes::firing` told its drain the listeners had pushed whenever the process-wide clock moved during the call. Under `cargo test`'s threads another test's world stamps meanwhile, so `fire_item_text` stopped after `ITEM_TEXT_READY` and left `ITEM_TEXT_TRANSLATION` queued. The bag-book test fires the lane once, so it saw only the first.
**Changed.** A firing's pushes are already its own thread's: `stamp()` nests them under the thread-local item firing. `firing` now compares a thread-local count of the stamps taken, so another thread's stamp moves nothing.
**Test.** `another_threads_stamp_is_no_push_of_the_firing` fails on main. The bag-book test under `--test-threads=16` failed 3 of 12 runs on main and none of 12 here.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The world mouseover tooltip was rebuilt once a frame from the frame's final state. 1.12.1 picks and publishes it at frame time (`0x481790` → `0x482090` → `0x492890`), but rebuilds it inside the packets, at its callbacks:
- a unit's name and creature records and its skinnable flag (`0x4919d0` via `0x491a00`, `0x5ff7be`);
- a game object's lock key (`0x52ad10`);
- a corpse's owner name (`0x52afe0`).
**Changed**
- Each rebuild runs at its callback and reads through the host. Other field changes don't rebuild, and a rebuild neither re-anchors nor raises UPDATE_MOUSEOVER_UNIT, as in 1.12.1.
- A record-less locked object shows "Locked" (`0x52aae2`).
- A `UnitName` miss signals UNIT_NAME_UPDATE when the name lands, whether or not the unit is held (`0x517290`), and the wait ends with the world session.
**Tests:** each rebuild read between two packets, the locked line, an out-of-view member's name update. Each fails on main.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The character doll's feed pushed the worn slots, weapon enchants and combat stats into the VM once a frame. A listener for an equip packet read the frame's last item, where 1.12.1 reads the item list at the call.
**Changed**
- Worn and bank-bag slots read live (`GetItemInSlot` `0x6228a0`, at `0x4c8cd3`), each slot built only when asked.
- `GetWeaponEnchantInfo` reads live (`0x4c97ca`), with six nils and no player (`0x4c9965`).
- Item countdowns read as of the listener's packet.
- The feed goes, along with its gate, the name cache's counter, and an ammo-hit `UNIT_INVENTORY_CHANGED` that 1.12.1 never raises: a cache hit signals nothing (`0x4c83ce`).
- New guards list the host's reads of handler-written state and its raw store reads.
**Tests:** an equip listener reads its own packet's item, and a line between two writes reads the first charges and enchant time. Each fails on main.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The movement, camera and mouselook verbs queued their work for the frame's input pass. 1.12.1 does it in the call: the tick, the stand-up and AFK clear (`0x514e23`), freelook's entry and leave, the sheath, the zoom and the loot close (`0x60e990`).
**Changed**
- Each verb acts inside its call.
- The mouse's primary bits live in one store, which IsMouselooking reads live (`0x514278`).
- Freelook entry moves the mouse focus to the world frame (`0x764dc0`). Its records walk no hover (`0x766250`).
- A press goes to the frame holding a button (`0x76637f`), and in freelook to the world (`0x492bd9`).
- The mouse turns the camera only while the command word holds a bit (`0x51440c`).
- `/follow` ends at the mouse hand-off (`0x5145fa`).
**Tests:** each verb's effect read on the next line of its chunk, the focus change's order, routing, and rebinding. Each fails on main.
Part of #2202.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Several small engine reads used copies a feed wrote once a frame, and the guard missed whole classes of such reads.
**Reference and changes**
- The "npc" token reads the interaction latch (`0x515c00`, `0x5f0251`).
- The camera pose loads at the interface load (`0x481b4e`).
- The display modes and the language skill are read at the call (`0x48bc90`, `0x4e3837`).
- Far sight:
- starts or ends at its field change (`0x5de144`, `0x5de15d`) and at the object's create (`0x5fbb1a`, `0x5d552d`);
- resets where our player is freed (`0x5dd33d`);
- its bit, not the camera, tears input down (`0x514537`).
- The guard now scans every system that writes the VM. It matches by full name and counts read-filled queues and writes through `&self`. That surfaced 10 honest rows.
**Tests:** each moved read between two packets, far sight at create, at loss, and across sessions, and every guard gap shown to fail.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The unit-token resolver's inputs were pushed into the VM once a frame. The verbs that read the push answered with the frame's last group, where 1.12.1 resolves at the call (`0x515970`).
**Changed**
- The feed is gone. The views' rewind and targeting walk the tokens' reach off app state (`ui_unit/reach.rs`).
- `InitiateTrade` resolves at the call (`0x48a120`).
- `UnitIsFriend` and `UnitCanCooperate` test membership through the host (`0x491900` → `0x4e80a0`, `0x4bafd0`).
- `GetPlayerMapPosition` reads at the call (`0x4a8610` → `0x4a7870`): a held Player's position, else the group record. The record is skipped when status bit 0x20 is set, and placed under its zone's own map, so a member in an instance reads (0,0).
**Tests:** trade, the friend test, and map position between two group lists, mid-frame joins, an instanced member, bit 0x20. Each fails on main.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since #1466 the world entry parks the glue VM. `load_ingame_ui_seated` unparks it and then returns early when a capture runs without the UI. That left the glue VM in the world, so a world capture such as a vista painted the login screen over the world.
**Changed:** in that branch, a glue VM's CVars fold and a boot VM is installed, the VM a reload already holds there. A reload is unchanged.
**Test:** `a_capture_without_the_ui_enters_the_world_on_a_boot_vm` enters the world under `CaptureMode` with the glue VM parked and checks the VM is in-game. It fails without the change (`Glue`).
Fixes#2819.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`cvar_app()` built a client from the window and capture variables without `ENV_LOCK`, while the capture, account-data and state-folder tests set them under it. Under `cargo test`, which runs a crate's tests on threads of one process, `a_locale_default_reaches_its_knob_at_load` saw another test's `WOW_WIN` and failed.
**Changed:** `cvar_app()` now holds `ENV_LOCK` for the client's life, with every variable the build and the load read cleared and a foreground run forced. Tests that set variables of their own build under the same hold.
**Tests:**
- On 16 threads, the cvars, capture, local-state and account-data tests together failed 20 of 21 runs before the change and 0 of 20 after.
- A new test sets `WOW_WIN` on another thread while a client builds. It fails on the old helper.
Fixes#2816.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The party feed pushed the roster to the interface once a frame, seeded a new interface late, and printed the ready check's summary a frame after its answer.
**Reference**
- Every party and raid getter reads the engine at the call (`0x4e86d0`, `0xbc6f48`, `[0xb713e0]`, `[0xb712a8]`).
- A `/reload`'s interface sees the group and lockouts from its first file (`0x490bd0`, `0x48fbf0`; `[0xb6e5e8]`).
- The ready check's not-ready and away lines print inside the answer's handler (`0x4bb310` → `0x49a870`).
- The friend list signals only from its own handlers (`0x5ae867`, `0x5ae92d`).
**Changed**
- The roster push and its catch-up are gone.
- The seed runs at the load, before the files.
- The ready-check lines print in place.
- The social feed's dead seed is gone.
**Tests:** a new interface's first lists signal at their packets, a reload's files read the lockouts, the ready-check lines between their packets, no stray list event.
Part of #2168 and #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cast state's getters read a copy a feed pushed once a frame, and a refused cast line that missed the item cache retried at frame time, so both lagged their packets.
**Reference**
- IsCurrentAction, IsAutoRepeatAction, SpellIsTargeting, SpellStopCasting and the targeting word's checks read the cast state at the call (`0x4e54c1`, `0x6e48e0`, `0xcecac0`).
- A reagent or totem refusal that misses the cache registers `ShowItemCastFailure` (`0x6e1e82`-`0x6e1ea4`, `0x6e29b0`). The item's answer shows it, with UNKNOWN on a refusal (`0x6e2a49`). With no item word no line ever shows (`0x55be5d`).
**Changed**
- The getters read through the host, and `feed_cast_reports` is gone.
- The refused line rides the item cache's callback list.
- The "Missing reagent" fallback that 1.12.1 never shows is removed.
**Tests:** the line between its neighbours for a record and for a refusal, the getters between packets, no line with no item word.
Part of #2168 and #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bar's last per-frame feed is gone, along with four mismatches with 1.12.1 its audit found.
**Reference**
- A macro or item slot's texture runs the attack and ranged face tests on its spell first (`0x4e6ab2`, `0x4e6acb`, `0x4e5a50`).
- A `/reload` keeps the bar in the engine, so the new interface reads it from its first file.
- SMSG_ACTION_BUTTONS only stages its words (`0x5e6680`), and the arrival loads them, skipping unknown spells (`0x5deaf5`).
- The usable walk asks with the bar's callback, behind its player and count gates (`0x4e5b30`, `0x4e5080`, `0x4e50ab`).
**Changed**
- Each of those, and a worldport's book no longer reruns the rank pass over loaded words.
- `feed_action_state` is deleted, and its tests read live.
**Tests:** an Attack macro's weapon icon, the bar during a reload's load, an unknown spell's empty slot, the walk's ask, the worldport's book.
Fixes#2810. Fixes#2811. Fixes#2812. Fixes#2813. Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UPDATE_INVENTORY_ALERTS fired from the character feed at frame time, and GetInventoryAlertStatus read its copy, so an alert lagged the durability or ammo packet that changed it.
**Reference:** `0x4c7ee0` recomputes the 12 statuses, including the temporary-enchant arm (`0x4c8035`). A break's tutorial fires inside the loop (`0x4b5390`), and the signal at `0x4c8085` on every pass. It runs from the item field watchers inside the update packet (`0x5d952a`, `0x5d97aa`, `0x5d9920`, `0x5d9b2f`, `0x5dee7a`) and at world entry. Any player's visible item also rebuilds the profession and craft lists (`0x5dee30`).
**Changed**
- The recompute runs at each watcher's step and reads live, including the temporary enchant.
- Its tutorial fires in place.
- Another player's visible item rebuilds both lists.
- The feed only stores.
**Tests:** a durability change's signal between its packets, the getter between packets, the enchant statuses, the tutorial ahead of the signal, the rebuilds.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A ready check's start line took the leader's name from the party roster, so it printed even when the client did not know the name, with an empty name if the roster lacked the leader.
**Reference:** the handler asks the name cache with no callback (`0x4ba50f` → `0x55f080`). A miss returns at once without a query (`0x55f120`-`0x55f125`, `0x55f221`), and the line is skipped (`0x4ba514`-`0x4ba516`). The deadline and READY_CHECK follow either way (`0x4ba535`, `0x4ba53a`).
**Changed:** the line reads the leader through the name cache, as the leave line does (#2000), and a miss prints nothing and sends nothing.
**Tests:** a cached leader prints the line before the popup, and an uncached one prints none and sends no query, while READY_CHECK still fires.
Fixes#2792.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bar's icon, count and consumable getters, its aura notifies and GameTooltip:SetAction read a copy a feed resolved once a frame, so a call between two packets read the frame's last state.
**Reference**
- GetActionTexture, GetActionCount and IsConsumableAction read at the call (`0x4e6a50`, `0x4e6c70`, `0x4e5250`), with the counts at `0xbc6390` zeroed at game entry (`0x4e4f50`).
- An item answer runs the bar's callback (`0x4e5b80` → `0x4e5c70`), firing ACTIONBAR_SLOT_CHANGED per slot.
- An aura add or removal notifies its slot around its log line (`0x4e5de0`).
- SetAction runs the cooldown query and attack test at the call (`0x5323a6`, `0x5323b4`).
**Changed**
- The getters and SetAction read through the host.
- The callback rides the item cache's list.
- The aura handlers notify in place.
- `feed_actions` keeps only the engine's refusals and a new VM's seed.
**Tests:** an item answer's slot event between its packets, the seed's counts, the aura notifies, SetAction inside its packet.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five engine calls queued their event for the VM's next tick, behind every packet, where the reference signals in place.
**Reference**
- A vendor sale clears the cursor and signals CURSOR_UPDATE inside the click (`0x5df5f0`, `0x494b60` → `0x495190`), unless the player lost control (`0x494b8d`).
- The landmarks rebuild and signal WORLD_MAP_UPDATE (`0x4a67a0`, `0x4a6ce4`) at the gossip marker's set, at its deadline, and at an arrival tested on a moved update (`0x6dac93`, `0x6d9411`, `0x6d9a4c`).
- World entry drops the marker but keeps its deadline (`0x6d8fb0`).
- The catalog's load signals nothing (`0x4a5d00`).
**Changed**
- The sale signals in its click.
- The landmarks signal on their own lane.
- World entry and `/reload` drop the marker.
- The seeds are silent.
- The world map's Deviation keeps only the world-enter sync.
**Tests:** the sale before the binding's next line, the gossip set between its packets, the deadline, a reload's marker.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`vm_parity` failed on Linux x86_64 on the `fix-libs deg_rad` case. #2660 made `math.deg` multiply by 180/pi, as `math_deg` does (`0x7fb14d`), but the case's round-trip summary kept the old divide's answer, 98 misses and `deg(rad(3)) == 3` true. With the multiply, IEEE doubles give 166 and false on every platform. macOS never saw it, because a platform case's whole answer is compared only on the reference platform.
Changed: both lines' summaries now read 166,false. The per-value items already matched.
Found in jhinzuo2/benilla-twow@3b40b46f.
Fixes#2804.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guards' source scan misses a raise reached through a helper in another module, so a frame-time feed could raise outside every lane unseen.
**Changed**
- In debug builds, every raise into the VM goes through one check: the immediate fire (`tick::fire_event_into`) and the queue (`Model::queue_event`, now its only push).
- A raise outside a script call, a lane or the VM's tick fails with the event and its caller chain, unless an allow entry names its guard row or reference place.
- The app's tests fail hard. A live run warns once per site.
- Its first run found `feed_char` raising UPDATE_INVENTORY_ALERTS at frame time, where the item field watchers signal it in the packet (`0x4c8085`). That is now a guard row.
**Tests:** a frame-time raise fails naming its caller, the same raise in a lane passes, and a live run reports each site once.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A follow ended at frame time, in `steer_follow`, and AUTOFOLLOW_END fired there, out of order with the packets that ended it.
**Reference:** `StopAutoMove` (`0x60fb60`) ends a follow inside the packet that ends it: our death, the attack-state swing, the taxi flag, a mover switch or release, a near teleport, a knockback, feign death, a stun, a monster move on us and far sight. It signals AUTOFOLLOW_END (`0x60fb84`), then releases forward and autorun. A near teleport and a knockback also close the loot window (`0x60e990`).
**Changed**
- One stop runs at each of those handlers, with END on the notice lane and the autorun clear behind its listeners.
- The loot window closes on a near teleport and a knockback.
- Each unit holds its client-controlled bit (`0x5fa636`).
- `feed_follow` is gone.
**Tests:** each end fires END between its neighbouring packets' events, and death's END comes before PLAYER_DEAD.
Part of #2168 and #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bar's usable pairs were stored by a per-frame diff, and EditMacro's and a bag update's refresh fired at frame end, so ACTIONBAR_UPDATE_USABLE and a slot's IsUsableAction lagged their cause.
**Reference**
- Each slot write stores its pair (`0x4e58e0`, `0x4e590a`).
- The refresh `0x4e5c00` runs at world entry, inside EditMacro (`0x4f132e`) and in the item watcher's run before BAG_UPDATE (`0x4f9374`).
- Game entry zeroes the cache (`0x4e4f50`).
- The reactive list keeps learn order (`0x5dea86`, `0x4b27b3`).
**Changed**
- Every writer stores its pair, and the refreshes run in place.
- The cache zeroes at game entry.
- The reactive list moves to its own resource, in wire order.
- The per-frame diff and its event queue are gone.
**Tests:** EditMacro's update inside the call, a bag run's between its packets, PlaceAction's listener reading its pair, the reactive order.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Craft window answered from a copy a feed refreshed once a frame, and rebuilt and signalled there.
**Reference**
- The build (`0x4f60c0`) zeroes its counts and, with no craft type open or no player, leaves before CRAFT_UPDATE (`0x4f661a`). The open, SMSG_PET_SPELLS, the record callback, learn and unlearn, and the item watchers rebuild it.
- The getters read at the call (`0x4f6c3b`, `0x4f6d52`, `0x4f6fe6`, `0x4f76b2`, `0x4f7a3c`).
- GetCraftDescription falls back to the taught spell's text (effect 36 or 57), with the pet flag (`0x4f7b53`).
**Changed**
- The build and the getters read through the host, and a build with no player empties silently.
- The description takes the fallback and the pet flag; the VM fills its templates in the call.
- `feed_craft` is gone; the trainer and the window share one token helper.
**Tests:** each getter read between two packets, CRAFT_UPDATE between its neighbours, the silent build, the description fallback.
Part of #2168 and #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the targeting cursor up, a unit a packet selected was parked and bound to the cursor a frame later. A spell's deferred swing at its go also skipped the attack validator.
**Reference**
- `SelectTarget` with the cursor up binds at once (`0x4935d5` → `0x6e5b40`) and selects nothing. The loot response, the two acquires and the go each reach it at their packet.
- The go's swing runs the validator `0x612df0` (`0x6131aa`): the actor checks, the select, then the reload from the selection (`0x6130c1`), with ERR_NO_ATTACK_TARGET or ERR_INVALID_ATTACK_TARGET.
**Changed**
- Each handler binds in place.
- The melee probe and the pet bind inside their press.
- The go runs the Attack action's validator and swings at the selection as reloaded.
**Tests:** the loot response binds between its neighbours, a stunned caster's go sends no swing, and with a cursor up the go binds one unit and swings at the selection.
Fixes#2113. Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The profession window answered from a copy a feed refreshed once a frame, and rebuilt and signalled there, so a getter or TRADE_SKILL_UPDATE between two packets read the frame's last state.
**Reference**
- An item answer runs the getter's or the build's record callback (`0x4fdc30`, `0x4fc8e0`), which rebuilds and signals at its packet.
- The build reads the book at its call (`0x4fca20`), and with no player it empties the list and signals nothing (`0x4fca52`, `0x4fcb1b` → `0x4fd170`).
- GetTradeSkillLine reads the open line at the call (`0x4fdd40`).
**Changed**
- The callbacks go on the item cache's list.
- The build and the getters read through the host.
- A build with no player empties the book silently.
- `feed_trade_skill` is gone.
**Tests:** the line getter between two packets, a reagent's answer between two falls, a learn banded at its packet, a product answer after `/reload`, and the silent empty build.
Part of #2168 and #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The error and info lines a packet raises showed from a per-frame feed, ahead of every lane's events, so they sat out of order against their own packets' signals.
**Reference:** each handler shows its line where it runs, through `DisplayError` (`0x496720`): a chat row, or UI_ERROR_MESSAGE or UI_INFO_MESSAGE by the message's kind.
**Changed**
- The lines ride a stamped lane, pushed at their handlers.
- Five orders now match the bytes: the guild event and invite, learn and rank-up, the party disband and the ready check.
- A rank-up shows its line with no player held (`0x5e6363`).
- A missing tame string shows ".", as `0x703bf0` does.
- A world session that ends before entry no longer leaves its lines for the next.
**Tests:** each line between its neighbouring packets' events, the rank-up line without a player, and the session that ends before entry.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The movement verbs and DropItemOnUnit queued their work for frame time, so a cast a movement press should cancel was still running on the script's next line.
**Reference**
- Every movement verb goes through `SetCommands` (`0x515090`). It sets the bit, and a new press in mask 0x10f0, or the press completing both mouse bits, cancels the cast inside the call (`0x51512a` → `0x6e4940`), then the channel, then runs the tick.
- ToggleAutoRun reads the bit at the call (`0x513de7`).
- DropItemOnUnit fills the trade or starts one in its call (`0x48da50`, `0x48da62`).
**Changed**
- With a host lent, the 23 movement verbs and Jump apply in the call.
- The autorun bit flips there.
- DropItemOnUnit applies in the call.
- The mouselook verbs still queue.
**Tests:** a press mid-cast cancels before the next line, two toggles in a frame cancel once, and a drop on a player trades in its call.
Part of #2202.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three windows still answered from a copy a feed refreshed once a frame, and fired their events there.
**Reference**
- The cursor's control gate reads our unit's taxi bit at the call (`0x494b97`-`0x494ba6`).
- PLAYER_CAMPING, PLAYER_QUITING, LOGOUT_CANCEL and the logout-failed line are raised by their packets' handlers (`0x5aaf17`, `0x5aaf72`, `0x5aaf26`).
- The death signals fire at their packets, and the corpse range is checked each frame (`0x492130`).
**Changed**
- The taxi gate reads the flag off our object.
- The logout replies go on the notice lane at their handlers.
- The death getters (GetCorpseRecoveryDelay and the rest) read live, and the death signals fire at their packets.
- `feed_on_taxi`, `feed_logout` and `feed_death` are gone.
The fake world server logs a session before answering its authentication, which a loaded test run read behind.
**Tests:** each getter read between two feeds, and each event between its neighbouring packets' events.
Part of #2168 and #2217.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The action bar's state getters and cooldown queries answered from a copy the feed refreshed once a frame, so a call made between feeds read the last frame's state.
Reference: each getter reads its slot at the call. A cooldown query that misses the item cache registers a callback on the record (`0x6e2f70`) and answers when the record arrives. A pet spell list derives its macros where it loads (`0x4bdae0` → `0x4f0ce0`). The go handler sweeps both cooldown lists every 120 s (`0x6e1910`); the insert sweeps nothing.
Changed: the bar getters (IsUsableAction, GetActionCooldown and the rest) resolve and read live. The cooldown query uses the record's callback list. The pet list derives its macros in its place. The sweep moves to the go.
Tests: each getter read between two feeds, the callback answered at the record, the macros derived after UPDATE_MACROS, and the swept GCD.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Several verbs did their work in a later drain: try-ons, the chat input's /target, /assist and /follow, RestartGx, and the engine CVar verbs (SetGamma, the video pairs, SetMultisampleFormat, the minimap zoom, the glue writes). A script's next line read the old state. LoadBindings also switched the current set, so a Character Specific tick then Cancel left the character set live.
Reference: a try-on reads the cache and lands or refuses in its call (`0x504540`, `0x504b2d`). RestartGx runs gxRestart synchronously (`0x48dab0` → `0x63ce00`), and SetMultisampleFormat restarts in its call (`0x48c79b`). LoadBindings never writes the current set (`0x4b8249`-`0x4b8252`).
Changed: those verbs apply in the call, and the slash commands call the stock verbs. The drains left at the frame each name why; SaveBindings only marks its set until shutdown (`0x4b6310`, `0x4b65e0`).
Tests: per verb, a same-chunk read of its effect.
Part of #2202.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tutorials were judged once a frame by a feed. A tutorial a packet raised fired behind every packet's events. Looting fired at a corpse's first sight, even one already dead at login, and Backpack fired before its item's record arrived.
Reference: each trigger fires in the handler or input that raises it.
- the vendor and trainer lists (`0x4fad32`, `0x4d74ae`);
- Looting on the dynamic flags' bit 0 rising (`0x600482`-`0x60049b`);
- the item tutorials after the received line, once the record is known (`0x491cec`);
- Raid Groups at the roster rebuild (`0x4ba60d`).
Changed: tutorials are a stamped lane, and each site triggers where it happens. TutorialsEnabled reads live. `feed_tutorials`, `judge_selections`, `watch_windows` and `TutorialEvent` go.
Tests: a packet's tutorial falls between neighbouring packets' events, and a ClearTutorials in an earlier listener stops a later trigger.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A profession's book opened from a frame feed, after the cast verb had returned, and its "Create All" repeat recast from a drain. A listener read the book shut inside the call that opened it, and a repeat on a recipe with a cooldown refused its own recast.
Reference: `TryCastSpell` opens the book inside the call (`0x6e4bce` → `ToggleTradeSkill` `0x4fc910` / `ToggleCraftWindow` `0x4f6000`). `HandleSpellGo` runs the repeat hook (`0x6e81e7`) before its cooldown inserts (`0x6e8425`). The repeat disarms only at a failure report, a failed cancel, or the close (`0x4fd790`'s four callers).
Changed:
- the toggles run in the call;
- the repeat runs in the go ahead of the cooldowns;
- `drain_trade_skill` and `open_trade_skill` go.
Tests:
- a listener's CastSpellByName shows the book between two packets;
- Create All recasts at the go, also with a cooldown.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bar's slot clears, rank-ups and learned-ability latches were rewritten once a frame by feeds. A listener inside an unlearn or a supersede read the old bar, and ACTIONBAR_SLOT_CHANGED fired ahead of every packet event.
Reference: the unlearn handler clears the spell's slots after the book removal (`0x4e5e20` at `0x5ea253`). The supersede's `0x4e5e60` clears then sets each slot, with a send and an ACTIONBAR_SLOT_CHANGED for each (`0x4e58e0`). The learn and unlearn set the latches (`0x4b262c`, `0x4b2c83`). Each game entry zeroes the bar (`0x48f5a4`).
Changed: those writes run in their handlers, with the signals on the notice lane. The rank pass's Deviation: now runs only where login slots arrive. The bar empties at game entry.
Tests: an unlearn's listener reads the slot cleared; a supersede sends clear then set; a relog sends nothing for the last character's bar.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WORLD_MAP_UPDATE for a newly explored zone was queued for the VM's next tick from a frame feed, so it fired behind every packet's events. A listener read the map's overlays before the explored bits were written.
Reference: `0x4a6430` arms a field watcher on the player's explored bytes (section 4, `+0xe6c`, `0x100` bytes). Its callback `0x4a6460` re-selects the shown map: `0x4a67a0` reads the bytes, rebuilds the overlay and landmark lists, and signals WORLD_MAP_UPDATE once (`0x4a6ce4`), inside the update's notify pass.
Changed: a field notice for the explored bytes, fired on the field-notices lane at its packet. It writes the bits, rebuilds the landmarks, then signals. The tick queue's row goes.
Tests: the event falls between neighbouring packets' events; a listener reads the new overlay; an update that moves no explored byte fires nothing.
Part of #2168.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>