diff --git a/CLAUDE.md b/CLAUDE.md index 7c6c4b57..a0a9af1b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -149,7 +149,13 @@ parse). See the plan doc for the retired-row ledger. **Campaign A — audio retail parity (`docs/plans/2026-08-08-audio-parity-campaign.md`) is CODE-COMPLETE 2026-08-08** with slices A1–A6 landed and listening-gate rounds user-driven; open tail: #358 (Ctrl+M mute chord never fires) and -the formal plan-status flip. +the formal plan-status flip. **Campaign CH — chat & interface-text retail +parity (`docs/plans/2026-08-09-chat-parity-campaign.md`) is CODE-COMPLETE +2026-08-09** with slices CH1–CH4 (colors, SpewBox interface text, side +channels, command registry) landed and dual-Opus-reviewed, pending the +connected user gate (test script: +`docs/research/2026-08-09-campaign-ch-test-script.md`); carried tail +#358–#363. **Placement cutover — C4 COMPLETE 2026-08-05, merged to main.** Every placement route now runs through the canonical residence + continuation- diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 11e26acb..a5a4eacf 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -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 ` (`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 diff --git a/docs/architecture/retail-divergence-register.md b/docs/architecture/retail-divergence-register.md index cf835bd1..48d20aa7 100644 --- a/docs/architecture/retail-divergence-register.md +++ b/docs/architecture/retail-divergence-register.md @@ -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: " 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 ` 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 | |---|---|---|---|---|---| diff --git a/docs/plans/2026-04-11-roadmap.md b/docs/plans/2026-04-11-roadmap.md index 12f8eded..7b0d552f 100644 --- a/docs/plans/2026-04-11-roadmap.md +++ b/docs/plans/2026-04-11-roadmap.md @@ -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 A1–A6 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 diff --git a/docs/plans/2026-08-09-chat-parity-campaign.md b/docs/plans/2026-08-09-chat-parity-campaign.md index 47c5d373..49361514 100644 --- a/docs/plans/2026-08-09-chat-parity-campaign.md +++ b/docs/plans/2026-08-09-chat-parity-campaign.md @@ -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: