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:
parent
090825e703
commit
724ef2d389
17 changed files with 853 additions and 85 deletions
|
|
@ -123,11 +123,23 @@ public sealed class RetailClientCommandCatalogTests
|
|||
[InlineData("/allegiance motd")]
|
||||
[InlineData("/allegiance")]
|
||||
[InlineData("/all officer add 2 Bob")]
|
||||
public void UnsupportedAllegianceSubcommand_FallsThroughToServerPassthrough(string input)
|
||||
public void UnsupportedAllegianceSubcommand_ShowsRetailRefusal_ClientSide(string input)
|
||||
{
|
||||
// Same Tier-1-class fix, applied to the allegiance management
|
||||
// dispatcher (TS-68): unrecognized subcommands reach ACE.
|
||||
Assert.False(RetailClientCommandCatalog.TryMatch(input, out _));
|
||||
// CH4 REJECT-review Blocker 1 (2026-08-09): unlike @house (whose
|
||||
// unrecognized subcommands correctly reach ACE, see the test
|
||||
// above), retail's own DoAllegiance NEVER falls through to
|
||||
// DoChannelCommand/the server for an unrecognized subcommand — it
|
||||
// claims the whole verb unconditionally and prints its own
|
||||
// client-local refusal (label_57da4b, 0x0057DA4B). The earlier
|
||||
// "falls through to server passthrough" behavior here was itself
|
||||
// the bug: an unmatched subcommand used to escape all the way to
|
||||
// the unregistered-tag channel fallback and broadcast to the
|
||||
// Allegiance chat channel.
|
||||
Assert.True(RetailClientCommandCatalog.TryMatch(input, out var match));
|
||||
Assert.False(match.HasValidArguments);
|
||||
Assert.Equal(
|
||||
"Please see @help Allegiance for more information on how to use this command.",
|
||||
match.InvalidArgumentsText);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -155,6 +167,9 @@ public sealed class RetailClientCommandCatalogTests
|
|||
[InlineData("/endurance", ClientCommandId.Endurance)]
|
||||
[InlineData("/speaker", ClientCommandId.Speaker)]
|
||||
[InlineData("/index", ClientCommandId.IndexChannels)]
|
||||
// CH4 REJECT-review nit 14 (2026-08-09): DoChannelIndex ignores argc —
|
||||
// "@index foo" sends the same request as bare "@index".
|
||||
[InlineData("/index foo", ClientCommandId.IndexChannels)]
|
||||
public void MissingAliasesSweep_Resolve(string input, ClientCommandId expected)
|
||||
{
|
||||
Assert.True(RetailClientCommandCatalog.TryMatch(input, out var match));
|
||||
|
|
@ -201,6 +216,11 @@ public sealed class RetailClientCommandCatalogTests
|
|||
[Theory]
|
||||
[InlineData("/permit add Bob", true)]
|
||||
[InlineData("/permit remove Bob", true)]
|
||||
// CH4 REJECT-review SHOULD-FIX 5 (2026-08-09): retail's DoPermit joins
|
||||
// every token after the mode word into the name (JoinArgsAsName), so a
|
||||
// multi-word character name is a VALID shape, not a rejected one.
|
||||
[InlineData("/permit add Aunt Agatha", true)]
|
||||
[InlineData("/permit remove Lord Gnarly Beard", true)]
|
||||
[InlineData("/permit add", false)]
|
||||
[InlineData("/permit maybe Bob", false)]
|
||||
public void Permit_ArgumentShape(string input, bool expectedValid)
|
||||
|
|
@ -222,8 +242,16 @@ public sealed class RetailClientCommandCatalogTests
|
|||
[Theory]
|
||||
[InlineData("/clist fellowship", true)]
|
||||
[InlineData("/on admin", true)]
|
||||
[InlineData("/off nonsense", false)]
|
||||
public void ChannelArgumentCommands_ResolveTagsAgainstRetailChannelTagTable(string input, bool expectedValid)
|
||||
// CH4 REJECT-review SHOULD-FIX 6 (2026-08-09): the catalog only
|
||||
// validates argument SHAPE (retail's argc != 1 check) — a resolved-but-
|
||||
// UNKNOWN single-token tag is now a VALID shape that reaches
|
||||
// ClientCommandController, which raises WeenieError 0x422 ("That
|
||||
// channel doesn't exist.") instead of the catalog silently rejecting
|
||||
// it with the wrong "Please specify the channel name." usage line.
|
||||
[InlineData("/off nonsense", true)]
|
||||
[InlineData("/clist", false)]
|
||||
[InlineData("/on fellowship extra", false)]
|
||||
public void ChannelArgumentCommands_RequireExactlyOneToken(string input, bool expectedValid)
|
||||
{
|
||||
Assert.True(RetailClientCommandCatalog.TryMatch(input, out var match));
|
||||
Assert.Equal(expectedValid, match.HasValidArguments);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue