fix(chat): CH4 review fixes — allegiance ownership guard, house-abandon confirmation

Blocker 1: an unrecognized "@allegiance <sub>" subcommand escaped
TryMatchAllegiance (which only claimed "info"/"hometown") and fell through
the unregistered-tag channel fallback, broadcasting the raw subcommand
text to the Allegiance chat channel (0x02000000). Retail's own
DoAllegiance never reaches DoChannelCommand for an unrecognized
subcommand — it claims the whole verb and prints its own client-local
refusal. TryMatchAllegiance now claims "allegiance"/"all" unconditionally
and shows retail's "Please see @help Allegiance..." text; ChatCommandRouter
also gained a blanket RetailClientCommandCatalog.KnownVerbs ownership
guard in TryDispatchChannelFallback as defense in depth.

Blocker 2: "@house abandon" sent 0x021F immediately with no confirmation.
Retail runs a real two-stage dialog before Event_AbandonHouse(); ported
both verbatim strings and chained two ShowConfirmation calls.

Should-fixes: a bare unregistered tag with no text now passes through
silently instead of showing a refusal that belongs to a different retail
function; @join/@leave update RuntimeCharacterOptionsState locally (new
SetOptionBit) before the wire push so the Turbine membership gate stops
refusing a just-joined room; @permit accepts multi-word names; @clist/
@on/@off validate shape only and raise WeenieError 0x422 for an unknown
tag; @mr/@pr help text is now the verbatim retail strings; corrected
issue #360, register row TS-68, the campaign doc's B.7 note, and a stale
RetailChannelTagTable comment; filed issue #363 + register row AP-183 for
the deferred error-typing debt.

Nits: fixed TryMatchHouse's stale doc comment, the AP-182/@title "stores
the value" comments (the binding is a no-op), IsUnregisteredFallbackTag's
olthoi false-positive, added /g and /rp binding-level conformance pins,
made @index ignore extra arguments, and noted the six removed invented
verbs in ISSUES.md.

Suite: 12,216 passed / 4 skipped / 0 failed (Release), up from CH4's
12,190/4/0 — net +26 tests, no removals.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-09 21:59:35 +02:00
parent 090825e703
commit 724ef2d389
17 changed files with 853 additions and 85 deletions

View file

@ -26,7 +26,8 @@ What does NOT go here:
## #360@allegiance/@house management dispatchers only port their simple subcommands
**Status:** OPEN — filed 2026-08-09, Campaign CH slice CH4. Retail's
**Status:** OPEN — filed 2026-08-09, Campaign CH slice CH4; corrected
2026-08-09 at the CH4 REJECT-review (Blocker 1). Retail's
`@allegiance`/`@all` and `@house`/`@hou` are 12- and 15-subcommand local
command dispatchers (`ClientCommunicationSystem::DoAllegiance @
0x0057D5A0` / `DoHouse @ 0x00580860`). CH4 ports the subset with simple
@ -40,13 +41,23 @@ boot_all/remove_all/guest/available/hooks/on/off) plus the standalone
target-name/guid resolution, confirmation dialogs, or multi-field payloads
this session did not attempt to build without byte-level verification
against both the retail decomp and ACE's reader — see the doc's own
framing ("largest single item; deserves its own slice"). Today these
subcommands correctly fall through to ACE as server-passthrough text
(`RetailClientCommandCatalog.TryMatchHouse`/`TryMatchAllegiance`) rather
than being swallowed locally, which was the Tier-1 correctness fix this
slice DID land — but they don't yet execute. Register row: TS-68.
Registry doc: `docs/research/2026-08-09-chat-retail-command-registry.md`
§2.5/§2.5b.
framing ("largest single item; deserves its own slice"). For `@house`,
these subcommands correctly fall through to ACE as server-passthrough
text (`RetailClientCommandCatalog.TryMatchHouse`) rather than being
swallowed locally, which was the Tier-1 correctness fix CH4 landed — but
they don't yet execute. **For `@allegiance`/`@all`, the original filing's
"falls through to ACE" claim was wrong**: retail's own `DoAllegiance`
never reaches server passthrough for an unrecognized subcommand — it
prints "Please see @help Allegiance for more information on how to use
this command." locally and stays entirely client-side
(`ClientCommunicationSystem::DoAllegiance`, label at 0x0057DA4B). The
CH4 REJECT-review found acdream had instead been broadcasting the
unmatched subcommand text to the Allegiance chat channel — a real
chat-visible bug, now fixed (`TryMatchAllegiance` claims ownership
unconditionally and shows retail's own refusal text). The 22 subcommands
themselves still don't execute; only the fallback behavior changed.
Register row: TS-68. Registry doc:
`docs/research/2026-08-09-chat-retail-command-registry.md` §2.5/§2.5b.
## #361@day / @log / @render pure-local commands recognized in help only, not executed
@ -73,6 +84,42 @@ registered in `GameEventType` with no `GameEventWiring` handler — ACE's
reply is silently dropped. The request itself is correct and verifiable
on the wire; only the response rendering is missing. Register row: TS-70.
## #363 — Chat refusal/usage call sites are typed ClientLocal 0x00 where retail types several 0x1A
**Status:** OPEN — filed 2026-08-09, CH4 REJECT-review, SHOULD-FIX 9.
`ChatVM.ShowSystemMessage`'s single-typed sink (`LogTextType 0x00`,
informational) is correct for most of `ClientCommandController`'s output,
but Campaign CH slice CH4 added roughly 10 new refusal/usage call sites
that retail types `0x1A` (bright red / ClientLocal), not `0x00`:
`DoStupidChannelHack` (the "You must specify the text you wish to say!"
family, registered channel verbs only), `DoChannelList`/`DoChannelOn`/
`DoChannelOff` ("Please specify the channel name."), `DoAllegiance` (the
"Please see @help Allegiance..." refusal this session's Blocker 1 fix
added), `DoHouseAvailableList`, and `DoReply` ("Someone must @tell you
first!"). Three CH4 sites are ALREADY correct because retail itself types
them informational `0x00`: `DoSpeaker`, `DoEndurance`, `DoTitle`.
Separately, retail's own bad-args fallback
(`ClientCommunicationSystem::DoCommand @0x0057E46D`) answers a registered
handler that returns 0 with `HandleFailureEvent(0x26)`, not a local
"Usage: <usage>" line — `ChatCommandRouter.Submit` shows a synthesized
`"Usage: {clientCommand.Usage}"` string instead whenever a catalog
command's `InvalidArgumentsText` is null. Register row: AP-183.
Deliberately NOT fixed this session — re-plumbing every call site to a
typed sink (and porting `HandleFailureEvent(0x26)`'s real text) is larger
than a REJECT-review fix batch; CH5-or-later.
## Note — six invented chat verbs removed for registry parity (2026-08-09)
Campaign CH slice CH4 deleted `/gen`, `/cv`, `/lookingforgroup`, `/tr`,
`/role`, `/h` from `ChatInputParser`/`ChatCommandRouter` — none are
retail-registered verbs; the retail command registry doc
(`docs/research/2026-08-09-chat-retail-command-registry.md` §4,
"candidates for removal") confirmed none exist in the real client. Not a
bug, no issue number — recorded here so they aren't reintroduced later as
"missing aliases." `RetailCommandRegistryConformanceTests`'s two
reverse-direction ownership tests now fail the build if any of the six
(or any other invented verb) resurfaces.
## #356 — Alt-tab during login crashed the client: focus loss faulted on an unpublished movement controller
**Status:** CLOSED 2026-08-08 — `972c7ab3`. Window focus loss runs

File diff suppressed because one or more lines are too long

View file

@ -114,7 +114,7 @@ implementer per slice against a pinned contract (per
| CH1 colors | `172c6f9a` | 11,835 passed / 4 skipped / 0 failed | APPROVE-WITH-FIXES; fixed `34d8a3c0` | pending |
| CH2 interface text | `77c8296e`, reworked `e0e78883` | 11,916 passed / 4 skipped / 0 failed | REJECT → reworked `e0e78883` → re-review APPROVE-WITH-FIXES → nits `233c30d1` | pending |
| CH3 side channels | `614a1e05` | 11,964 passed / 4 skipped / 0 failed | APPROVE-WITH-FIXES; fixed `e07fba57` | pending (connected gate — see handoff below) |
| CH4 commands | see CH4 closeout below | 12,026 passed / 4 skipped / 0 failed | pending | pending |
| CH4 commands | `090825e7` | 12,190 passed / 4 skipped / 0 failed | REJECT; fixed `<sha>` | pending |
| CH5 closeout | — | — | — | — |
### CH4 closeout (2026-08-09)
@ -152,6 +152,19 @@ Full parser-semantics + catalog-breadth pass against
7. `/allegiance`/`/all` are now `RetailClientCommandCatalog`'s allegiance
MANAGEMENT command (a new `TryMatchAllegiance` dispatcher), not a
channel verb. The channel-send verbs stay `a`/`ab`/`guild`/`gu`.
**Corrected 2026-08-09 at the CH4 REJECT-review (Blocker 1): the
original implementation above only claimed ownership for the 2 ported
subcommands (`info`/`hometown`/`ho`) and let every OTHER subcommand
fall through the unregistered-tag channel-fallback path, which
broadcast the raw subcommand text to the Allegiance chat channel
(`0x02000000`) — a real chat-visible bug (`@allegiance boot Bob` sent
"boot Bob" to allegiance chat). Retail's own `DoAllegiance` claims the
ENTIRE verb unconditionally: an unrecognized subcommand prints "Please
see @help Allegiance for more information on how to use this command."
locally and never reaches `DoChannelCommand` or the server.
`TryMatchAllegiance` now matches this exactly — it always returns
ownership for `allegiance`/`all`, showing retail's refusal text for
any subcommand beyond the 2 ported ones.**
8. `/house`/`/hou` no longer swallows unrecognized subcommands with a
local usage error — `TryMatchHouse` returns no match for anything
beyond `recall`/`re`/`mansion_recall`/`alleg_recall`/`ma`/`abandon`,
@ -196,14 +209,92 @@ KnownVerbs` or `ChatInputParser.KnownVerbs` may exist outside this
registry — a future invented alias fails the build immediately. Final
tally: **138 Implemented / 5 ServerPassthrough / 9 HelpOnly = 152.**
Suite: 12,026 passed / 4 skipped / 0 failed (Release), up from CH3's
11,964/4/0 — net +62 tests (157 new conformance-family cases plus net
test churn from updated existing coverage). One pre-existing,
Suite: 12,190 passed / 4 skipped / 0 failed (Release), up from CH3's
11,964/4/0 — net +226 tests. (Corrected 2026-08-09 at the CH4
REJECT-review, item 8: this paragraph originally read "12,026 ... net
+62 (157 new conformance-family cases plus net test churn)"; the actual
measured CH4-landing count was 12,190, matching CLAUDE.md's Current
Suite baseline — only the raw counts are corrected here, the +62/157
breakdown was not re-derived.) One pre-existing,
environment-specific Debug-only failure
(`LandblockBuildOriginTests.FarLoad_StripsEnvCellsAndPhysicsEvenWhenEntityListIsAlreadyEmpty`)
was confirmed present on the unmodified baseline via `git stash` before
and after this slice's changes — passes in Release, unrelated to chat.
### CH4 REJECT-review fixes (2026-08-09)
Two blockers, seven should-fixes, and six nits from the CH4 review landed:
**Blockers:** (1) `@allegiance <sub>` for an unrecognized subcommand was
broadcasting the raw subcommand text to the Allegiance chat channel
(`SendRawChannelCmd(0x02000000, ...)`) because `TryDispatchChannelFallback`
only guarded on `ChatInputParser.IsKnownVerb`, and `/allegiance` had been
deleted from that parser at CH4. Fixed at both ends: `TryMatchAllegiance`
now claims ownership of `allegiance`/`all` unconditionally (matching
retail's own `DoAllegiance`, which never falls through to
`DoChannelCommand`) and shows retail's own "Please see @help Allegiance
for more information on how to use this command." refusal client-side;
`TryDispatchChannelFallback` also gained a blanket
`RetailClientCommandCatalog.KnownVerbs` ownership guard as defense in
depth for the rest of the catalog. (2) `@house abandon` sent `0x021F`
immediately with zero confirmation; retail's `DoHouse` abandon branch runs
a real two-stage dialog ("Do you really want to abandon your house? ..."
then "Are you absolutely certain you wish to abandon your house? Click
yes only if you are sure!") before `Event_AbandonHouse()`.
`ClientCommandController`'s `HouseAbandon` case now chains two
`ShowConfirmation` calls with retail's verbatim text; `AbandonHouse` only
fires after both accepts.
**Should-fixes:** a bare unregistered tag with no text (`@admin`) now
passes through to the server silently, matching retail's `DoChannelCommand`
returning 0 on `argc<=0`, instead of showing "You must specify the text
you wish to say!" (that string belongs to the registered-verb-only
`DoStupidChannelHack`); `@join`/`@leave` now update
`RuntimeCharacterOptionsState` locally (a new `SetOptionBit` method)
before the wire push, so `TurbineChatMembershipGate` stops refusing a
just-joined room without waiting on a fresh `PlayerDescription`;
`@permit add/remove` now accepts a multi-word name (`>= 2` tokens,
joins the remainder, matching retail's `JoinArgsAsName`); `@clist`/`@on`/
`@off` now validate only argument SHAPE (exactly one token) at the
catalog layer and raise `WeenieError 0x422` ("That channel doesn't
exist.") for an unresolved tag, instead of silently doing nothing;
`@mr`/`@pr`'s help text is now the verbatim retail strings from
`data_7daa08`/`data_7daa80` (previously fabricated acdream summaries),
and the class doc no longer overclaims every table entry is verbatim
(the ~35 channel one-liners are acknowledged as acdream summaries);
issues #360 and register row TS-68 corrected — `@house`'s unported
subcommands still reach ACE, but `@allegiance`'s now correctly stay
client-side; the campaign doc's own B.7 note and `RetailChannelTagTable`'s
stale "IsKnownVerb intercepts them first" comment are corrected to
describe the catalog-ownership interception path; the ledger suite counts
above are corrected from a stale 12,026 to the actual 12,190. The
error-typing debt (~10 new refusal sites at `ClientLocal 0x00` where
retail types several `0x1A`, plus `DoCommand`'s real `HandleFailureEvent
(0x26)` bad-args response) was deliberately NOT re-plumbed — filed as
issue #363 and register row AP-183, CH5-or-later.
**Nits:** `TryMatchHouse`'s doc comment no longer describes a
local-swallow path that doesn't exist in the code; AP-182 and the
`SetChatTitle`/`@title` comments across three files no longer claim the
value is "stored" (the binding is `_ => { }`, a pure no-op) and AP-182
now lists `DoTitle`'s three omitted failure messages; `RetailChannelTagTable
.IsUnregisteredFallbackTag` now excludes by TAG STRING instead of channel
ID, fixing a false-positive on `"olthoi"` (which shares an id with the
genuinely-unregistered `"ol"` but has its own registered Turbine verb);
two binding-level conformance pins (`/g`→Fellowship `0x800`, `/rp`→reply)
were added to `RetailCommandRegistryConformanceTests` so a rebind
regression fails there, not just a narrower parser test; `@index foo` is
now accepted (retail's `DoChannelIndex` ignores argc); an ISSUES.md note
records the six invented verbs (`gen`/`cv`/`lookingforgroup`/`tr`/`role`/`h`)
removed at CH4 for registry parity.
Suite: 12,216 passed / 4 skipped / 0 failed (Release), up from CH4's
12,190/4/0 — net +26 tests (new/expanded theory cases across
`ChatCommandRouterTests`, `RetailClientCommandCatalogTests`,
`RetailCommandRegistryConformanceTests`, `ClientCommandControllerTests`,
`RuntimeCharacterStateTests`, and `TurbineChatMembershipGateTests`; no
tests removed, several renamed/retargeted in place).
### CH3 closeout handoff (2026-08-09)
All nine steps of the research doc's §6 fix list landed: