fix(chat): CH6a/b rework — grip media, retail window-id model, floaty fixture

Applies docs/research/2026-08-10-ch6ab-review-findings.md in full:

- BLOCKER 1: UiResizeGrip now carries its ElementInfo/resolve pair and
  draws its own authored DirectState media (a synthetic parameterless
  grip still draws nothing, preserving existing resize-drag tests).
  DatWidgetFactory.BuildResizeGrip threads resolve through. All seven
  live grips on the main chat window now resolve a non-zero sprite,
  restoring the visible borders/corners CH6a silently dropped.

- SHOULD-FIX 2: ChatWindowState gains BroadcastTargetWindow, a sentinel
  distinct from every real window id (0-4), fixing the bug where the
  main window's explicit-addressing branch coincided with the broadcast
  check (both were literal 0). SetFilter's main-window no-op is dropped
  — the main window's filter is now genuinely settable. ChatWindowController
  .Bind takes a ChatWindowState (the same canonical instance the floating
  windows already share) and GetTranscriptLines builds a real accept
  predicate instead of accept:null. Verified safe: ClientLocal (0x1A)
  never reaches ChatLog (AddText routes it to the SpewBox and returns),
  so nothing observable regresses.

- SHOULD-FIX 3: UiButton.SuppressSelfToggle stops the four chat-window
  indicator buttons (DAT property 0x0B=true, no retail click handler)
  from flipping their own Selected mirror on a stray click.

- SHOULD-FIX 4: generated and committed chat_floaty_2100005b.json from
  the real installed dats; added the permanent RetailLayoutFixtureGenerator
  entry. All three flagged FloatingChatWindowController assumptions
  (input field, title bar, close button) are confirmed correct against
  real data — no controller code changes needed. New finding: unlike the
  main window, ALL EIGHT floaty border/corner elements are live Type-9
  grips (the floaty's own title bar is its move handle), so a floaty
  window resizes from every edge and corner.

- SHOULD-FIX 5: register row AP-189 documents the shared-500-entry/
  200-line-tail vs retail's per-window 10,000-line scrollback depth gap.

- NITs 1-5: documented the filter-persistence-only-on-/saveautoui
  asymmetry and the reconnect-preserves-filters intent; corrected the
  research doc's modifier-mask mislabel and the "ONLY function" false
  superlative; moved WrapText off ChatWindowController onto
  ChatTranscriptRenderer, closing the circular dependency.

Full Release suite: 12,420 passed / 4 skipped / 0 failed (baseline
12,392/4/0 at 22020ef2; net +28 tests, zero regressions).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-10 13:09:32 +02:00
parent 56b84deeab
commit 1aa7709988
23 changed files with 6284 additions and 248 deletions

View file

@ -245,10 +245,10 @@ implementer per slice against a pinned contract (per
| CH6a main-window layout + 8-grip resize | (this commit) | 12,317 passed / 4 skipped / 0 failed | pending (no subagent review pass this session — implementer-only) | pending — needs the next in-client round (items H/I round 1, item 6 round 2) |
| CH6b/CH6c floating windows + opacity | superseded — split below | — | — | superseded |
| User gate round 2 | (this commit) | 12,267 passed / 4 skipped / 0 failed | — | items 2/4/5 fixed this commit, item 3 confirmed-fixed, item 6 folded into CH6a's spec, item 1 NOT reproduced (see "User gate — round 2" below) |
| CH6a main-window layout + 8-grip resize | `1fd51543` | 12,317 passed / 4 skipped / 0 failed | pending | pending — landed same day as round 2 |
| CH6a main-window layout + 8-grip resize | `1fd51543`, reworked (this commit) | 12,420 passed / 4 skipped / 0 failed | REJECT (docs/research/2026-08-10-ch6ab-review-findings.md, BLOCKER 1) → reworked (this commit) | pending — needs the next in-client round to confirm the border/corner art now renders |
| Jump-in-air root cause (round-2 item 1, resolved) | `a5a7eb4f` | Runtime tests 1,323/0 | — | round-3 probe evidence pinpointed a missing `OnInterfaceText` wire on the production controller-commit path (`RuntimeLocalPlayerMovementState.CommitRuntimeOwnedController`); FIXED, regression test added |
| User gate round 3 | (this commit) | Debug (all projects): 12,329 passed / 4 skipped / 1 failed (pre-existing #351 Debug-only flake — reproduces identically on the pristine pre-round-3 commit, not a regression); Release (every project reachable while a live `AcDream.App.exe` client — PID 15064, must not be killed per project policy — holds its own Release binaries locked, blocking `AcDream.App`/`AcDream.App.Tests`/`AcDream.Core.Tests` specifically): `AcDream.UI.Abstractions.Tests` (the layer this round's `/help` fix lives in) 867/867, plus `Core.Net.Tests` 823/823, `Runtime.Tests` 1,323/1,323, `Content.Tests` 130/130, `Headless.Tests` 89/89, `Bake.Tests` 15/15, `Cli.Tests` 4/4 — all 0 failed | — | findings (a)-(c) fixed this commit — SpewBox flush-top + retail dat font, `/help`/`/help death` exact retail print sequence (see "User gate — round 3" below) |
| CH6b floating windows 14 | (this commit) | 12,392 passed / 4 skipped / 0 failed | REJECT (docs/research/2026-08-10-ch6ab-review-findings.md); rework in progress | pending — no client launches this session (hard constraint); needs the next connected round for keybind/mirror/filter visual confirmation |
| CH6b floating windows 14 | `22020ef2`, reworked (this commit) | 12,420 passed / 4 skipped / 0 failed | REJECT (docs/research/2026-08-10-ch6ab-review-findings.md) → reworked (this commit) — SHOULD-FIXES 2/3/4/5 + NITs 1-5 applied | pending — no client launches this session (hard constraint); needs the next connected round for keybind/mirror/filter visual confirmation, plus the new 0x2100005B fixture's resolved-type assumptions |
| CH6c opacity | not started | — | — | not started |
### CH4 closeout (2026-08-09)
@ -722,3 +722,98 @@ retail LayoutDesc dumps) to find the smallest font id actually CONFIRMED
in use by any retail UI import acdream has ported, rather than merely
present in the DAT. `0x40000025` (11px) was both the global minimum across
those fixtures AND the chat window's own minimum — no tie-break needed.
## CH6a/b REJECT-review rework (2026-08-10)
Applied everything in `docs/research/2026-08-10-ch6ab-review-findings.md`
under the session's hard constraints (no subagents, no client launches).
The retail research itself held up under independent re-derivation
(masks, Alt+1..4, pure-mirror indicators all CONFIRMED); the rework was
entirely against the FIX list, not the research.
- **BLOCKER 1 (CH6a — invisible borders).** `UiResizeGrip` (`src/AcDream.App/UI/UiResizeGrip.cs`)
gained a second constructor carrying its `ElementInfo`/sprite-resolve
pair and an `OnDraw` override that stamps the active DirectState sprite
exactly like `UiDatElement` — a synthetic (parameterless) grip still
draws nothing, matching every existing resize-drag unit test.
`DatWidgetFactory.BuildResizeGrip` now threads `resolve` through.
`SpriteFile` exposes the raw dat sprite id for conformance tests without
needing a live GL context; `ChatLayoutConformanceTests
.MountedChatWindow_LiveGrip_ResolvesNonZeroSprite` pins all seven live
grips against the committed `chat_2100006f.json` fixture.
- **SHOULD-FIX 2 (CH6b — window-id model).** `ChatWindowState` gained
`BroadcastTargetWindow` (a sentinel distinct from every real window id
`0`-`4`) so `ShouldDisplay`'s explicit-addressing branch can no longer
coincide with the broadcast check for the main window (id `0`).
`SetFilter`'s main-window no-op is dropped — the main window's filter is
now genuinely settable, matching retail's options-page-driven main
filter. `ChatWindowController.Bind` gained a `ChatWindowState
windowFilters` parameter (the same canonical
`RuntimeCommunicationState.ChatWindows` instance the floating windows
already read) and `GetTranscriptLines` now builds a real accept
predicate from it instead of `accept: null` — verified safe: `0x1A`
(`ClientLocal`) never reaches `ChatLog` in the first place
(`RuntimeCommunicationState.AddText` routes it to the SpewBox and
returns before `Chat.OnSystemMessage`), so excluding it from the main
filter has zero effect on WeenieError/refusal text. Every production
gameplay chat type (Speech/Tell/Social/Fellowship/Allegiance/Turbine
rooms) stays within the default filter's covered bits. Register row
AP-189 documents the resulting shallower shared-log scrollback depth
(SHOULD-FIX 5, below) as the one behavioral residual.
- **SHOULD-FIX 3 (CH6b — indicator self-toggle).** `UiButton` gained
`SuppressSelfToggle`, checked alongside `ToggleBehavior` at the
press-release toggle site; `ChatWindowController.Bind` sets it on all
four chat-window indicator buttons (which carry DAT property `0x0B` =
true but have no retail click handler). New `UiButtonTests
.SuppressSelfToggle_PressReleaseDoesNotFlipSelected` and an extended
`MountedChatWindow_IndicatorButtons_ImportVisibleAndInert` (now presses
and releases the real fixture's indicator, asserting `Selected`
unchanged) replace the "wrong level" `OnClick == null` check.
- **SHOULD-FIX 4 (CH6b — 0x2100005B never dumped).** The floaty layout
DAT dir at `%USERPROFILE%\Documents\Asheron's Call\` (the default
`ConformanceDats.ResolveDatDir()`/`RetailLayoutFixtureGenerator` path)
IS reachable in this environment — `chat_floaty_2100005b.json` (171 KB,
5,473 lines) was generated for real and committed;
`RetailLayoutFixtureGenerator.Layouts` gained the permanent entry for
future regenerations. All three flagged untested assumptions in
`FloatingChatWindowController` turned out CORRECT against the real
fixture — no controller code changed: `0x10000016` is Type 12 with
property `0x16` (Editable) = true, resolving to `UiField`; `0x100004D9`
is Type 12 with NO `0x16`, resolving to `UiText`; `0x1000052A` is Type 1,
resolving to `UiButton`. One extra finding beyond the three named
assumptions: unlike the main window (whose plain top strip is a Type-2
Dragbar move handle because the main window has no title bar), the
floaty window's move handle is its own title bar group (`0x10000529`,
Type 2) — so ALL EIGHT of the floaty's border/corner elements are live
Type-9 grips, not seven; a floaty window resizes from every edge and
corner. New `FloatingChatLayoutConformanceTests.cs` pins the resolved
widget types and extends BLOCKER 1's sprite-non-zero guard to all eight
floaty grips.
- **SHOULD-FIX 5 (register row).** AP-189 files the shared-500-entry/
200-line-tail vs. retail's per-window 10,000-line log scrollback-depth
divergence — the accumulate-while-closed and independent-per-window-
scroll BEHAVIORS both reproduce correctly; only the numeric DEPTH ceiling
differs.
- **NITs 1-5.** N1: documented the filter-persistence-only-on-`/saveautoui`
asymmetry on `RetailUiRuntime.SaveChatWindowFilters`, deferred to CH6e.
N2: documented why `ChatWindows.ResetToDefaults()` only runs in
`RuntimeCommunicationState.Dispose` (full teardown), not on reconnect —
deliberate, matches window-geometry persistence. N3: replaced the §1.3
UNVERIFIED modifier-mapping hedge and the "`0x00000002` = shift" mislabel
with `retail-default.keymap.txt`'s own `MetaKeys` legend (Shift=`0x1`,
Ctrl=`0x2`, Alt=`0x4`, Win=`0x8`) — no code change, `KeyBindings` already
had `ModifierMask.Alt` right. N4: fixed the false "ONLY function that
branches on `idMessage == 1`" superlative in §1.4 and
`ChatWindowController.SetIndicatorOpen`'s doc
(`gmFloatyChatUI::ListenToElementMessage @0x004CE330` also does); the
substantive "no case for the indicator ids" claim stands. N5: moved
`WrapText`/`WrapSingleLine` off `ChatWindowController` onto
`ChatTranscriptRenderer` (closing the circular dependency where
`BuildLines` called back into one of its own two consumers); updated the
two unrelated external callers (`IndicatorDetailText.cs`,
`ItemAppraisalReport.cs`) and moved the WrapText tests into the new
`ChatTranscriptRendererTests.cs`.
Full Release suite: 12,420 passed / 4 skipped / 0 failed (baseline
12,392/4/0 at `22020ef2`; net +28 tests, all new coverage from this
rework, zero regressions).