docs: Campaign CH closeout sweep — CODE-COMPLETE pending user gate

CH5: flip the campaign plan's status header from ACTIVE to CODE-COMPLETE,
correct the CH4 ledger row's suite count to its final 12,221 (was showing
the pre-review-fix 12,190) and fill in the CH5 row, and add a closeout
paragraph for the previously-undocumented 5d247d55 re-review round.
Register sweep found one drift: the TS section header claimed 42 active
rows against an actual recount of 40 (TS-66 is retired/struck-through and
was miscounted as active) — corrected. AP/AD/IA/UN section counts,
AP-175..183, AP-176 retirement, and UN-9's deletion all verified
consistent, no other changes. Cross-referenced issues #359-#363 to the
campaign doc. Extended CLAUDE.md's Current-state Campaign-status sentence
to record Campaign CH's CODE-COMPLETE status and carried tail. Added a
Campaign CH entry (plus a missing Campaign A entry) to the roadmap's
shipped-campaign summary block, matching the Campaign V/N/P paragraph
format.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-09 22:34:54 +02:00
parent 813cc51f9a
commit 38f0c0defc
5 changed files with 98 additions and 18 deletions

View file

@ -75,6 +75,9 @@ 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.
**Campaign:** `docs/plans/2026-08-09-chat-parity-campaign.md` (Campaign CH,
slice CH4).
## #361@day / @log / @render pure-local commands recognized in help only, not executed
**Status:** OPEN — filed 2026-08-09, Campaign CH slice CH4. Three
@ -88,6 +91,9 @@ recognized by `/help <verb>` (`RetailCommandHelpTable`) with retail's own
extracted help text, but fall through to server passthrough on execution.
Register row: TS-69.
**Campaign:** `docs/plans/2026-08-09-chat-parity-campaign.md` (Campaign CH,
slice CH4).
## #362 — Four new CH4 outbound requests have no inbound response handler
**Status:** OPEN — filed 2026-08-09, Campaign CH slice CH4. `@index`,
@ -100,6 +106,9 @@ 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.
**Campaign:** `docs/plans/2026-08-09-chat-parity-campaign.md` (Campaign CH,
slice CH4).
## #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.
@ -124,6 +133,9 @@ 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.
**Campaign:** `docs/plans/2026-08-09-chat-parity-campaign.md` (Campaign CH,
CH4 REJECT-review).
## Note — six invented chat verbs removed for registry parity (2026-08-09)
Campaign CH slice CH4 deleted `/gen`, `/cv`, `/lookingforgroup`, `/tr`,
@ -208,6 +220,9 @@ uninvolved bystander.
caller) and skip the append when it matches `victimGuid` or `killerGuid`,
matching retail's participant check.
**Campaign:** `docs/plans/2026-08-09-chat-parity-campaign.md` (Campaign CH,
filed at the CH1 review).
## #357 — Login stalls: reveal reaches ready=True but the player is never placed; UI + sky render, world never opens
**Status:** CLOSED 2026-08-08 — root-caused and fixed same session (see the

View file

@ -340,7 +340,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
| AP-183 | **Filed 2026-08-09 at the CH4 REJECT-review, item 9.** Roughly 10 chat refusal/usage call sites Campaign CH slice CH4 added route through `ChatVM.ShowSystemMessage`'s single `LogTextType 0x00` (ClientLocal-informational) sink; retail types several of them `0x1A` (bright red / genuine refusal) instead: `DoStupidChannelHack` (the "You must specify the text you wish to say!" family, registered channel verbs), `DoChannelList`/`DoChannelOn`/`DoChannelOff` ("Please specify the channel name."), `DoAllegiance` (the "Please see @help Allegiance..." refusal this session's Blocker 1 added), `DoHouseAvailableList`, and `DoReply` ("Someone must @tell you first!"). Three CH4 sites are already correct at `0x00` because retail itself types them informational: `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` synthesizes a `"Usage: {clientCommand.Usage}"` string instead whenever a catalog command's `InvalidArgumentsText` is null. Filed as issue #363; deliberately NOT re-plumbed this session (re-typing every call site is larger than a REJECT-review fix batch). `src/AcDream.UI.Abstractions/Panels/Chat/ChatVM.cs` (`ShowSystemMessage`); `src/AcDream.UI.Abstractions/Panels/Chat/ChatCommandRouter.cs` (`Submit`'s `Usage:` fallback) | Every one of these sites shows correctly-worded text in the correctly-shaped chat log entry, just at the wrong color/destination classification — a real but low-severity divergence from retail's exact on-screen presentation | A user comparing acdream's chat window side-by-side with retail for one of these specific refusals sees the wrong color (informational white/default instead of bright red) or, for the generic "Usage:" fallback, different WORDING than retail's `HandleFailureEvent(0x26)` text entirely | `ClientCommunicationSystem::DoStupidChannelHack @ 0x0057B144`; `DoChannelList @ 0x0057A9B0`; `DoChannelOn @ 0x0057AA80`; `DoChannelOff @ 0x0057AB50`; `DoAllegiance @ 0x0057D5A0`; `DoHouseAvailableList @ 0x00570510`; `DoReply @ 0x00577910`; `DoCommand @ 0x0057E46D` (`HandleFailureEvent(0x26)`) |
| AP-182 | **Filed 2026-08-09 (Campaign CH slice CH4); corrected 2026-08-09 at the CH4 REJECT-review (nit 11).** `@title <text>` is wired to a pure no-op — `LiveSessionRuntimeFactory`'s `SetChatTitle` binding is `_ => { }`; the requested title is neither stored nor consumed anywhere (the original filing's "stores the value locally" claim was false). This matches retail's own silent success (no confirmation text was recovered at the `DoTitle` success site, so a no-visible-effect accept is exactly as faithful as a stored-but-unread value would be). Also omitted: `DoTitle`'s three local failure messages — no title given, "You must provide a new title for the window."; length over 99 characters, "Window title length cannot exceed 100 characters."; and wrong source window (`m_idCurrentCommandSource` 1 or 8), "This command must be issued from a popup chat window." — acdream's catalog validator (`ClientCommandId.SetChatTitle`, `AnyArguments`) accepts any argument shape and never raises any of the three. `src/AcDream.App/Net/LiveSessionRuntimeFactory.cs` (`SetChatTitle`) | Retail's chat window presumably re-renders its title bar text; acdream's chat window has no title bar at all under the current retained-UI import, so there is nothing to visually diverge from yet | Once a titled chat-window chrome is built, `@title` needs to be re-wired to it — today it is a pure no-op, and the three failure messages above are silently absent | `ClientCommunicationSystem::DoTitle @ 0x0057A640` |
## 4. Temporary stopgap (TS) — 42 active rows (TS-68/TS-69/TS-70 filed 2026-08-09, Campaign CH slice CH4 — the deferred allegiance/house subcommand dispatchers, the three unported pure-local commands (day/log/render), and the four unparsed inbound GameEvent responses for CH4's new outbound requests; TS-66/TS-67 filed and TS-29 retired 2026-08-08, Campaign A slice A5 — the region ambient system landed, so TS-29's ambient half is ported and its music half turned out to have nothing to port; TS-66 is the omitted `seen_outside` interior case and TS-67 the in-plane contribution weight. TS-64/TS-65 filed 2026-08-08, Campaign A slice A2 — TS-64 the two unimplemented retail sound preferences (unfocused-app silence, pan disable) plus the three enable bools; TS-65 the volume-squared quirk, applied on the ambient path where two lanes byte-confirmed it and deliberately NOT on the hook path where the pre-multiplying overload is unpinned. TS-62/TS-63 filed 2026-08-02, continuation-executor slice; TS-4 and TS-8 retired 2026-07-31; Campaign P's goal-enumerated physics stopgaps are now zero. TS-4's graph/flat Path-6 branches match retail's foot SetCollide/Adjusted and head CollisionNormal/Collided split with no BSP-layer sliding-normal write; TS-8's live 0x02C2 carries its complete StatMod through the canonical enchantment record and updates effective stats immediately. Campaign P P7 2026-07-30: TS-25 retired — outbound stance has shipped via RawState.CurrentStyle since #219; TS-24 re-argued to AD-57; TS-40 re-argued to AD-58; TS-35 retired at P5; earlier same campaign: TS-1/TS-5/TS-23/TS-46 retired by ports; TS-23 retired 2026-07-30 at Campaign P Slice P3 — every mover-flags call site (local player world-entry ×2, remote DR sweep ×2, remote teleport, ordinary movers) now ORs in the mover's real PK/PKLite/Impenetrable `ObjectInfoState` bits via the new `ClientObjectTable`-backed `EntityCollisionFlagsExt.ResolveMoverPvpState`**narrative corrected 2026-08-03 (#297): "real" only became true at #297. Until then the bits existed but the source `PublicWeenieBitfield` was frozen at CreateObject, so every one of those sites read a stale value for the whole session. The site enumeration is also incomplete: `RuntimeSetPositionMoverPreparation.cs:183-188` is a SEVENTH mover-flags site that decodes `record.Snapshot.ObjectDescriptionFlags` directly rather than calling `ResolveMoverPvpState`, and it also derives `ObjectInfoState.IsPlayer` from the PWD bit, contradicting `EntityCollisionFlags.cs:119-123`'s claim that every site uses a GUID-prefix heuristic. See AP-134.** — and `PlayerWeenie.JumpStaminaCost`'s `pk` parameter reads the real `PlayerKillerStatus`/`LastPkAttackTimestamp` pair against a 20-second window instead of a hardcoded `false`; the non-PK invariant (every ACE default-created character) is bit-identical to the pre-P3 value since `ResolveMoverPvpState` and the PK-timer predicate both resolve to a no-op for `PublicWeenieBitfield` absent/0; TS-46 retired 2026-07-30 at Campaign P Slice P3 — the Setup's verbatim ≤2-sphere list (`CPhysicsObj::transition` 0x00512dc0 → `SPHEREPATH::init_sphere` 0x0050c670) now seeds the sweep for the local player, remote dead-reckoning, and ordinary movers alike, replacing the two-scalar (radius, height) capsule reconstruction; remote/ordinary step-up/step-down are now Setup-derived (`CPartArray::GetStepUpHeight`/`GetStepDownHeight`, 0x005180d0/0x005180f0, ×ObjScale) instead of a hardcoded 0.4 m, closing both residuals the row named; TS-5 retired 2026-07-30 at Campaign P Slice P1 — real burden-gated CanJump + real JumpStaminaCost, both decomp-verbatim; TS-1 retired 2026-07-30 at Campaign P Slice P2 — the row was stale; the EdgeSlide → PrecipiceSlide/CliffSlide chain is already a real, tested port; TS-57..TS-61 filed 2026-07-29 during Campaign N — no outbound RejectRetransmit; TS-27 narrowed same slice to the inbound direction) + TS-37 historical note (TS-20 retired 2026-07-16 — the later named-retail audit disproved the proposed DrawingBSP polygon filter; TS-37 is a retired-row historical note, not an active count; TS-39 retired R5-V3 — sticky seams bound to the ported PositionManager/StickyManager, radii threaded; TS-45 retired 2026-07-07 — hand-rolled `SphereCollision` replaced by the faithful CSphere family port, fixing the player-vs-monster crowd wedge; TS-3 retired 2026-07-07 — `frames_stationary_fall` accounting ported in the #182 verbatim UpdateObjectInternal rebuild, fixing the airborne falling-animation wedge; TS-41 retired 2026-07-07 — SERVERVEL synth-velocity remote body-drive replaced by the retail interp catch-up + unconditional MovementManager::UseTime, the remote-creature de-overlap #184; TS-42 retired 2026-07-19 — semantic animation completion now precedes the ordered Target/Movement/PartArray/Position tail; TS-44 narrowed again 2026-07-19 — complete orientation joined interpolation, only during-stick enqueue suppression remains)
## 4. Temporary stopgap (TS) — 40 active rows (TS-68/TS-69/TS-70 filed 2026-08-09, Campaign CH slice CH4 — the deferred allegiance/house subcommand dispatchers, the three unported pure-local commands (day/log/render), and the four unparsed inbound GameEvent responses for CH4's new outbound requests; TS-66/TS-67 filed and TS-29 retired 2026-08-08, Campaign A slice A5 — the region ambient system landed, so TS-29's ambient half is ported and its music half turned out to have nothing to port; TS-66 is the omitted `seen_outside` interior case and TS-67 the in-plane contribution weight. TS-64/TS-65 filed 2026-08-08, Campaign A slice A2 — TS-64 the two unimplemented retail sound preferences (unfocused-app silence, pan disable) plus the three enable bools; TS-65 the volume-squared quirk, applied on the ambient path where two lanes byte-confirmed it and deliberately NOT on the hook path where the pre-multiplying overload is unpinned. TS-62/TS-63 filed 2026-08-02, continuation-executor slice; TS-4 and TS-8 retired 2026-07-31; Campaign P's goal-enumerated physics stopgaps are now zero. TS-4's graph/flat Path-6 branches match retail's foot SetCollide/Adjusted and head CollisionNormal/Collided split with no BSP-layer sliding-normal write; TS-8's live 0x02C2 carries its complete StatMod through the canonical enchantment record and updates effective stats immediately. Campaign P P7 2026-07-30: TS-25 retired — outbound stance has shipped via RawState.CurrentStyle since #219; TS-24 re-argued to AD-57; TS-40 re-argued to AD-58; TS-35 retired at P5; earlier same campaign: TS-1/TS-5/TS-23/TS-46 retired by ports; TS-23 retired 2026-07-30 at Campaign P Slice P3 — every mover-flags call site (local player world-entry ×2, remote DR sweep ×2, remote teleport, ordinary movers) now ORs in the mover's real PK/PKLite/Impenetrable `ObjectInfoState` bits via the new `ClientObjectTable`-backed `EntityCollisionFlagsExt.ResolveMoverPvpState`**narrative corrected 2026-08-03 (#297): "real" only became true at #297. Until then the bits existed but the source `PublicWeenieBitfield` was frozen at CreateObject, so every one of those sites read a stale value for the whole session. The site enumeration is also incomplete: `RuntimeSetPositionMoverPreparation.cs:183-188` is a SEVENTH mover-flags site that decodes `record.Snapshot.ObjectDescriptionFlags` directly rather than calling `ResolveMoverPvpState`, and it also derives `ObjectInfoState.IsPlayer` from the PWD bit, contradicting `EntityCollisionFlags.cs:119-123`'s claim that every site uses a GUID-prefix heuristic. See AP-134.** — and `PlayerWeenie.JumpStaminaCost`'s `pk` parameter reads the real `PlayerKillerStatus`/`LastPkAttackTimestamp` pair against a 20-second window instead of a hardcoded `false`; the non-PK invariant (every ACE default-created character) is bit-identical to the pre-P3 value since `ResolveMoverPvpState` and the PK-timer predicate both resolve to a no-op for `PublicWeenieBitfield` absent/0; TS-46 retired 2026-07-30 at Campaign P Slice P3 — the Setup's verbatim ≤2-sphere list (`CPhysicsObj::transition` 0x00512dc0 → `SPHEREPATH::init_sphere` 0x0050c670) now seeds the sweep for the local player, remote dead-reckoning, and ordinary movers alike, replacing the two-scalar (radius, height) capsule reconstruction; remote/ordinary step-up/step-down are now Setup-derived (`CPartArray::GetStepUpHeight`/`GetStepDownHeight`, 0x005180d0/0x005180f0, ×ObjScale) instead of a hardcoded 0.4 m, closing both residuals the row named; TS-5 retired 2026-07-30 at Campaign P Slice P1 — real burden-gated CanJump + real JumpStaminaCost, both decomp-verbatim; TS-1 retired 2026-07-30 at Campaign P Slice P2 — the row was stale; the EdgeSlide → PrecipiceSlide/CliffSlide chain is already a real, tested port; TS-57..TS-61 filed 2026-07-29 during Campaign N — no outbound RejectRetransmit; TS-27 narrowed same slice to the inbound direction) + TS-37 historical note (TS-20 retired 2026-07-16 — the later named-retail audit disproved the proposed DrawingBSP polygon filter; TS-37 is a retired-row historical note, not an active count; TS-39 retired R5-V3 — sticky seams bound to the ported PositionManager/StickyManager, radii threaded; TS-45 retired 2026-07-07 — hand-rolled `SphereCollision` replaced by the faithful CSphere family port, fixing the player-vs-monster crowd wedge; TS-3 retired 2026-07-07 — `frames_stationary_fall` accounting ported in the #182 verbatim UpdateObjectInternal rebuild, fixing the airborne falling-animation wedge; TS-41 retired 2026-07-07 — SERVERVEL synth-velocity remote body-drive replaced by the retail interp catch-up + unconditional MovementManager::UseTime, the remote-creature de-overlap #184; TS-42 retired 2026-07-19 — semantic animation completion now precedes the ordered Target/Movement/PartArray/Position tail; TS-44 narrowed again 2026-07-19 — complete orientation joined interpolation, only during-stick enqueue suppression remains)
| # | Divergence | Where (file:line) | Why it is safe / justified | Risk if assumption breaks | Retail oracle |
|---|---|---|---|---|---|

View file

@ -62,6 +62,30 @@ crowd, two-client remote/door/portal, and shallow-water behavior. The user
waived the general sweep and explicitly deferred the barred-house gate as
#274. The later exact-location #273 tight-gap gate is now fixed and accepted.
**Campaign A — audio retail parity (CODE-COMPLETE 2026-08-08):** ported
retail's CPU-side 2-D pan+gain audio model (animation-hook sounds, the
`0xF750` server sound channel, PhysicsScripts, the interface sound bank,
and the region ambient system); found and corrected three false claims in
the old E.2 row (no 3-D pool, gain-based eviction, weighted variant
picking) and deleted the music API (retail has none). Slices A1A6 landed;
open tail is #358 (Ctrl+M mute chord never fires) and the formal
plan-status flip. Plan:
[`2026-08-08-audio-parity-campaign.md`](2026-08-08-audio-parity-campaign.md).
**Campaign CH — chat & interface-text retail parity (CODE-COMPLETE
2026-08-09, pending the connected user gate):** four implementation
slices closing the chat surface's biggest retail-parity gaps ahead of the
friend-alpha: CH1 the complete 34-value `LogTextType` color table, CH2
the on-screen SpewBox interface text (jump-refusal class, WeenieError
routing table), CH3 side-channel membership/wire/echo parity (Turbine
rooms, the legacy family, self-echo), and CH4 command registry completion
(138 of 152 retail verbs now execute locally). Every slice landed a
dual-Opus review (retail faithfulness + architecture) before its gate;
full Release suite 12,221 passed / 4 skipped / 0 failed. Plan and ledger:
[`2026-08-09-chat-parity-campaign.md`](2026-08-09-chat-parity-campaign.md);
in-client acceptance script:
[`2026-08-09-campaign-ch-test-script.md`](../research/2026-08-09-campaign-ch-test-script.md).
**Remaining physics-divergence closeout (ACTIVE, checkpoint 2026-08-03):** the user then
authorized retirement of the remaining proven collision/placement gaps before
vendor work resumes. Nested retry, edge/StepDown/Path-6 ordering, exact cell

View file

@ -1,19 +1,24 @@
# Campaign CH — chat & interface-text retail parity
**Status:** ACTIVE 2026-08-09 — CH1 complete (implementation `172c6f9a` +
review fixes `34d8a3c0`); CH2 CODE-COMPLETE and CLOSED pending the user
gate (`77c8296e`, REJECT-reviewed at
`docs/research/2026-08-09-ch2-review-findings.md`, reworked `e0e78883`,
re-reviewed APPROVE-WITH-FIXES with nits applied this commit) — the sole
outstanding item is the in-client user gate (jump-in-air / jump-loaded
refusals showing on-screen, not in chat). CH3 (side channels)
CODE-COMPLETE, pending the connected user gate — see the CH3 row below and
`docs/research/2026-08-09-chat-side-channels-vs-ace.md`. CH4 (command
registry completion) CODE-COMPLETE — see the CH4 closeout below; 138 of
152 registry verbs now execute locally, 5 are deliberately deferred
(issues #360/#361/#362, register rows TS-68/TS-69/TS-70), and 9 are
retail's own null-handler help-only nodes. Pending the in-client user
gate (command spot-checks per the campaign's overall gate list).
**Status:** CODE-COMPLETE 2026-08-09 — all four implementation slices
landed and dual-Opus-reviewed; pending the connected user gate (test
script: `docs/research/2026-08-09-campaign-ch-test-script.md`). CH1
complete (implementation `172c6f9a` + review fixes `34d8a3c0`); CH2
CODE-COMPLETE and CLOSED pending the user gate (`77c8296e`,
REJECT-reviewed at `docs/research/2026-08-09-ch2-review-findings.md`,
reworked `e0e78883`, re-reviewed APPROVE-WITH-FIXES with nits applied at
`233c30d1`) — the sole outstanding item is the in-client user gate
(jump-in-air / jump-loaded refusals showing on-screen, not in chat). CH3
(side channels) CODE-COMPLETE, pending the connected user gate — see the
CH3 row below and `docs/research/2026-08-09-chat-side-channels-vs-ace.md`.
CH4 (command registry completion) CODE-COMPLETE, closed at `5d247d55`
see the CH4 closeout below; 138 of 152 registry verbs now execute
locally, 5 are deliberately deferred (issues #360/#361/#362, register
rows TS-68/TS-69/TS-70), and 9 are retail's own null-handler help-only
nodes. CH5 (this closeout sweep) is COMPLETE — plan/register/ISSUES/
CLAUDE.md/roadmap ledger flip plus the chat memory digest. Pending the
in-client user gate (colors, side channels, on-screen text, command
spot-checks per the test script).
**Why now:** first track of the alpha-release program (chat is the most
visible daily surface for the friend-alpha). User-directed 2026-08-09.
@ -114,8 +119,8 @@ 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 | `090825e7` | 12,190 passed / 4 skipped / 0 failed | REJECT; fixed `724ef2d3`; re-review APPROVE-WITH-FIXES; closed `5d247d55` | pending |
| CH5 closeout | — | — | — | — |
| CH4 commands | `090825e7` | 12,221 passed / 4 skipped / 0 failed | REJECT; fixed `724ef2d3`; re-review APPROVE-WITH-FIXES; closed `5d247d55` | pending |
| CH5 closeout | (this commit) | — (docs/memory only, no build) | — | pending (connected gate — see test script) |
### CH4 closeout (2026-08-09)
@ -295,6 +300,36 @@ Suite: 12,216 passed / 4 skipped / 0 failed (Release), up from CH4's
`RuntimeCharacterStateTests`, and `TurbineChatMembershipGateTests`; no
tests removed, several renamed/retargeted in place).
### CH4 re-review fixes — closed at `5d247d55` (2026-08-09)
A second, smaller review round found three more should-fixes after
`724ef2d3` landed: (1) `RetailDialogFactory.CloseDialog`'s queued branch
ran `DialogDone` — whose callback can synchronously reopen a new dialog
under the SAME queue key, exactly what the two-stage house-abandon
confirmation does — then called `OpenNextDialog`, which did an
unconditional `Dictionary.Add` on a key the reentrant dialog had already
re-occupied; retail's `HashTable::add` tolerates the duplicate, `Dictionary`
throws. `OpenNextDialog` now returns early when the queue key is already
active. (2) `@join`/`@leave` wrote the local `RuntimeCharacterOptionsState`
bit before sending, but the Settings Chat toggles reached a second binding
(`SendSingleCharacterOption`) that only sent the wire message, leaving the
Turbine membership gate stale until the next `PlayerDescription`;
`LiveSessionRuntimeFactory.CreateCommandBindings` now shares one local
function for both entrances. (3) TS-68/#360's wording was corrected again —
retail's `DoAllegiance` dispatcher table EXECUTES the nine
boot/ban/officer/title/motd/name/lock/house/chat/broadcast subcommands
locally through their own handlers; acdream shows the unrecognized-
subcommand refusal for all nine pending the #360 port. What matches
retail is the ownership rule (the verb never reaches
`DoChannelCommand`/the server), not the subcommand behavior itself — the
inaccurate "now matches this" claims were removed from both the register
row and the issue.
Suite: 12,221 passed / 4 skipped / 0 failed (Release), up from CH4's
12,216/4/0 — net +5 tests, no removals. This is the CH4 slice's final
number; the ledger row above and CLAUDE.md's Current Suite baseline both
carry it.
### CH3 closeout handoff (2026-08-09)
All nine steps of the research doc's §6 fix list landed: