acdream/docs/research/2026-08-10-ch6ab-review-findings.md
Erik 8b554d4de4 docs: CH6a+CH6b review REJECT findings — invisible borders, wrong window-id model
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 12:32:09 +02:00

7.4 KiB
Raw Blame History

CH6a+CH6b review findings — 1fd51543 + 22020ef2 (REJECT; rework list)

Dual-lens Opus review, 2026-08-10. The retail research held up under independent re-derivation (Type-2/Type-9 correction, filter masks, Alt+1..4, pure-mirror indicators all CONFIRMED); the rework list below is what did not.

BLOCKER 1 — CH6a: chat window loses 7 of its 8 authored border pieces

UiResizeGrip : UiElement (UiResizeGrip.cs:33) inherits the no-op UiElement.OnDraw and is constructed WITHOUT the ElementInfo/resolve pair (DatWidgetFactory.cs:270-278) — the seven Type-9 grips render NOTHING. Pre-CH6a, Type 9 fell through to UiDatElement (whose class doc names resize grips explicitly). Same commit also hides the eight _Locked media twins (ChatWindowController.cs:239-245) and switches to Chrome = RetailWindowChrome.Imported (RetailWindowFrame.Mount:133-142 makes outerFrame = content, no wrapper chrome). Fixture proof: all 8 border elements carry real art (0x06006129/2A/2B/2C/2D family); the window root has NO background image. Net: only the 400×5 top strip (Type 2 → UiDatElement) draws — no left/right/bottom edges, no corners.

Fix: UiResizeGrip derives from UiDatElement (or carries info+resolve and draws its active state's media), keeping ClickThrough = false; BuildResizeGrip(info)BuildResizeGrip(info, resolve). Conformance assertion: each of the seven grips resolves a non-zero sprite.

SHOULD-FIX 2 — CH6b: wrong window-id model asserted as decomp fact

ChatWindowState.cs:31-42 claims UpdateFromPlayerModule @0x004F3920 early-returns for the MAIN window and overloads id 0 as both "main" and "broadcast". Decomp refutes both: PostInit @0x004F3DD0 switches on m_eWindowID - 1 (case 0→0xFBFFFFFF, 1→0x101C, 2→0x40C00, 3→0x80000, 4→0x78000000, 7→0xFBFFFFFF); gmChatOptionsUI::InitOptions @0x0049FC60 builds five per-window filter blocks with SetUserData window ids 8 (main, default 0xFBFFFFFF @0x0049FDC9), then 2/3/4/5 (@0x0049FF4E, :FF71, :FF93, @0x004A0000 default 0x78000000). Retail: main window is m_eWindowID == 8; floaties are 25; m_eWindowID == 0 means UNAUTHORED (ctor default — what the UpdateFromPlayerModule guard is for); wire arg5 == 0 is a pure broadcast sentinel, never a window id.

Consequences: the MASKS are correct once re-indexed (confirmed); the main window's filter IS user-settable in retail (first block on the options page, with a dedicated high-dword Society child @0x0049FEFB — so 0x20 Society is opt-in on main; acdream's accept: null main view over-displays it today); SetFilter(0,…) hard no-op (:119) blocks the future options UI; the 0-overloading makes AP-180's m_idCurrentCommandSource routing inexpressible. ShouldDisplay_MainWindow_ShowsEveryBroadcastRegardlessOfSeededFilter (:137-146) pins the wrong behavior including 0x1A displaying in main.

Fix: separate the broadcast sentinel from the main-window id — either carry retail's real ids (main 8, floaties 25) in ChatWindowState, or keep 04 internally plus an explicit BroadcastTargetWindow constant distinct from every id; drop the SetFilter main no-op; give the main window a real accept predicate (default 0xFBFFFFFF, i.e. everything except 0x1A and with the high dword zeroed so Society is opt-in) in ChatWindowController.GetTranscriptLines; correct the class doc; re-point the test.

SHOULD-FIX 3 — CH6b: indicator buttons self-toggle on click

UiButton.cs:438-439: _pressed && _pointerOver && Enabled && ToggleBehavior → _selected = !_selected; the fixture shows 0x10000522/0x10000523 carry DAT property 0x0B = true, so a click flips Highlight↔Normal art with no visibility change — the mirror lies until the next real toggle. Retail: clicking does nothing (0x100005220x10000525 appear ONLY in RecvNotice_SetPanelVisibility @0x004CCD80). The existing test asserts OnClick == null (wrong level) on a synthetic fixture without 0x0B. Fix: a SuppressSelfToggle opt-out checked at UiButton.cs:438, set in ChatWindowController.Bind with the @0x004CDA80 citation; test presses/releases an indicator built WITH 0x0B=true, state unchanged.

SHOULD-FIX 4 — CH6b: 0x2100005B never dumped (research doc's explicit instruction)

No chat_floaty_2100005b.json fixture; FloatingChatWindowControllerTests is fully synthetic with Type-3 stand-ins. Three untested production assumptions: FloatingChatWindowController.cs:122 (FindElement(InputId) as UiField — if 0x10000016 isn't Type-12 with Editable 0x16, all four windows mount non-interactive), :199 (TitleBarId is UiText — if it imports as UiDatElement the "Chat N" title never renders), :211 (CloseButtonId is UiButton — no test at all). The dump's widget_kind is media-presence-driven and cannot settle element types. Fix: add 0x2100005B to RetailLayoutFixtureGenerator.Layouts, commit the fixture, conformance tests pinning the resolved widget types of 0x10000016, 0x10000019, 0x100004D9, 0x1000052A, and the border/corner element types (also determines whether floaties resize from real grips).

SHOULD-FIX 5 — CH6b: missing register row for the shared-transcript model

Retail: per-ChatInterface m_chatLog, truncated at 10,000 (RecvNotice_DisplayFinalStringInfo @0x004F4711 → TruncateChatLog); a closed window keeps accumulating (gmFloatyMainChatUI::SetVisible @0x004CE9B0 never unregisters the handler). acdream: one 500-entry ChatLog with a 200-line display tail each window filters — accumulate- while-closed and per-window scroll fall out correctly, but the EFFECTIVE per-window scrollback depth differs (a fellowship-only window retains up to 10,000 fellowship lines in retail; here only those inside the last 200 shared lines). Fix: file the register row (AP series) describing the depth divergence.

NITs

N1 — filter persistence saves only from /saveautoui (SaveLayout()), while geometry/visibility auto-save; harmless today, note for CH6e. N2 — ChatWindows.ResetToDefaults() called only in Dispose; not resetting across reconnect is likely correct but undocumented — add the one-line comment (deliberate, user preference survives reconnect). N3 — research doc §1.3 still says UNVERIFIED for the modifier decode (CH6b resolved it: MetaKeys 1=Shift, 2=Ctrl, 3=Alt→mask 0x4, 4=Win — also fix the §1.3 "0x00000002 = shift" mislabel). N4 — §1.4's "the ONLY function that branches on idMessage == 1" superlative is false (gmFloatyChatUI::ListenToElementMessage @0x004CE330 does too); the substantive claim stands; fix the wording. N5 — ChatTranscriptRenderer.BuildLines calls back into ChatWindowController.WrapText; move WrapText into the renderer to close the circular dependency.

CONFIRMED-OK (do not churn)

CH6a layout geometry vs the independent 2026-06-25 dump; the Type-2/Type-9 correction; DecodeBorderLocation vs StartMouseResizing @0x0046B7E0; deleted compensations have no survivors and each removal is DAT-justified; cursor ids byte-exact; AP-185 accurate; the UiRoot grip/dragbar priority change is retail-correct. CH6b masks (via the options page, independent of PostInit); Alt+1..4 proven; pure-mirror indicators (the finding, not the implementation); closed-window accumulation reproduced; SpewBox 0x1A exclusion survives everywhere; ChatTranscriptRenderer behavior-identical for main; layering clean (Core presentation-free, single canonical instance, zero env reads, zero GameWindow changes); input pipeline conventions followed; visibility chokepoint genuine; AP-187/AP-188/#369 accurate.