CT-B4 was filed as "the plain-text session chat log, path and rotation
UNKNOWN, needs a live check." Both unknowns dissolve once you read the
handler: there is no automatic session log. Retail's @log is a COMMAND.
DoSetOutput @0x0057E4F0 takes a filename, StartCopyOutputToFile @0x0057C8A0
does the fopen(name, "a+"), and running it again with no argument closes it.
Nothing rotates because it appends forever, and nothing has a fixed path
because the player names the file.
The path question that DOES exist — where a bare name lands — was answered
all along by retail's own help text, which CH4 extracted verbatim into our
help table a fortnight ago and nobody read: "a log file named Aclog.txt in
your Asheron's Call directory." A blocked question sat on top of a committed
answer.
We cannot use the install directory: the launcher replaces it atomically on
update, so a log written there is wiped by the next update or blocks it. The
client's own log directory is the equivalent that survives. Rooted paths are
honoured verbatim, as retail's fopen would. Register CT-5.
The verb was registered in the help table but NOT in the command catalog, so
/log printed help and did nothing — and the CH4 conformance registry recorded
it as a "server passthrough" precisely because that shape is indistinguishable
from an unimplemented client command. It never went on the wire at all. Both
are corrected, with the totals moved in the same commit rather than left to
drift.
Moving it into the catalog also moves which help table answers for it, so
retail's real text moved to the catalog-verb table in the same change. Without
that, /help log would have silently started printing acdream's own invented
one-line summary — caught by the coverage test, and now pinned by a test that
names the text.
All five replies are byte-decoded from the PDB-paired binary rather than read
off Binary Ninja's previews, which truncate at ~33 characters and would have
lost the second half of every one of them (including the two spaces retail
puts after "Copying chat to %s.").
The writer attaches on OPEN, not at startup — retail's help is explicit that
only what appears after the command is copied — and detaches from the
transcript it actually attached to, so a session teardown cannot leave a live
handler writing into a file the player believes is closed. What gets written
is the composed display line with the shared timestamp, because retail's
fprintf sits inside AddTextToScroll: downstream of composition, upstream of
glyph layout. Logging the raw entry text would have produced a file of bare
fragments with no speakers.
acdream's logs carry no inline tag markup where retail's do, since tags live
beside the text as spans here rather than inside it. Registered as CT-6 rather
than reconstructed purely to write it to a file.
Register: CT-5, CT-6.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Item 3 (#364): every honesty marker is now gone from user-visible /help
text. AllegianceOverview/HouseOverview's "[IMPLEMENTED]" tags and trailing
"Subcommands NOT marked..." sentences, and Day/Log/Render/Motd's appended
"NOT YET IMPLEMENTED in acdream" tails, are removed; the underlying retail
text is corrected/completed against the pseudo-C's own pristine
consolidated data dumps (Log and Motd had been silently truncated; Render
was entirely acdream-authored and is replaced with the real retail usage
string). The three PARTIAL /help group topics (channels/chatting/commands)
are now COMPLETE verbatim listings: HelpStupidChannelHack's three
"vtable slot" operands, previously believed undecodable, are the same
pooled/mislabeled-data artifact this campaign has hit before (AP-113's
precedent) — reading the function's own disassembly for the push imm32
preceding each constructor call resolves all three directly. messagetypes
is now a real ported construction (IsLegalChannel's 14-id whitelist +
LogTextTypeToString's name table + the exact join/wrap format) instead of
an acdream summary. Register row AP-184 retired.
Item 5: the main window's 1/2/3/4 indicator buttons now toggle their
floating chat window on click, per the user's retail memory overruling
the earlier decomp-only reading. UIElement_Button::HandleButtonClick has
its own generic click-driven action dispatch (property 0x12) reaching the
same DoVisibilityToggleAction the Alt+1..4 keybinds use; the button
fixture confirms this half is genuinely armed, but the floating-window
fixture authors no matching listener-registration property, so the
generic mechanism has no proven target in the data on hand. Per
CLAUDE.md, the user's retail memory is the axiom regardless:
ChatWindowController.BindIndicatorClicks wires each indicator's click
through the same ToggleFloatingChatWindow chokepoint the keybinds use,
as explicit user-directed retail behavior. SetIndicatorOpen stays the
sole writer of the Selected mirror so the visual stays consistent
through the click round trip.
Full reconciliation in docs/research/2026-08-09-chat-retail-window-shell.md
§1.4. Campaign plan gets the round-4 findings section; items 1+2
(text-style) are under parallel research, item 4 passed, item 6 deferred
to the settings track.
Suite: 12,579 passed / 4 skipped / 0 failed (Release, complete solution),
up from baseline 12,553/4/0 — net +26 tests, zero regressions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SHOULD-FIX 1: RetailClientCommandCatalog's ~45 catalog leaf verbs were
showing acdream-authored Summary text for /help <verb> instead of
retail's own Detail_HelpType(2) text. Byte-swept every Help* handler
against the PDB-paired acclient.exe (verified MATCH), confirmed each
Detail/Summary branch by reading the actual decompiled if/else shape
(address order and string length both proved unreliable alone), and
fixed a sweep_weenie_strings.py 800-char truncation bug that silently
dropped several longer Detail branches. Resolved every ambiguous
CmdHashData-registered verb (hor/hr/hom/hoa/alh/ah/friends_add/
friends_remove/squelch/unsquelch) by reading for Binary Ninja's
nullptr-4th-arg decompiler artifact instead of trusting it. Coverage:
42 of 47 distinct catalog Definitions verbatim-extracted, 4
confirmed-null (index/clist/on/off register with a genuinely null help
pointer — DoHelp falls to UnknownCommand for these, now reproduced),
1 honest UNVERIFIED (messagetypes builds its text from a runtime enum
table, not a static string). ChatCommandRouter now prefers retail
Detail text over the catalog summary; RetailCommandHelpTable's class
doc no longer overclaims its own scope.
SHOULD-FIX 2: extracted the a5a7eb4f-class OnInterfaceText wiring into
a testable CreateChatViewModel method and added
ComposedChatViewModelWiresOnInterfaceTextToSpewBox, which the prior
FakeFactory-based test suite could never exercise.
SHOULD-FIX 3: retires register row AP-113. DoLifestone/DoMarketplace
print their own 0x1A refusal text (byte-recovered, UTF-16LE) instead
of falling through to the generic 0x26 fallback; ChatCommandRouter's
comment corrected to state the fallback's real scope.
SHOULD-FIX 4: corrected the divergence register's stale AP section
header sentence about AP-190's opacity default (refuted by cc582899).
NITs: (a) HeadlessStaticStateAudit routes through the injected
HeadlessDiagnosticWriter instead of Console.WriteLine; (b) a bounded
300-pump liveness diagnostic on the IsQuiescent conductor gate (no
retry, no behavior change); (c) fixed the #365 hydration test's doc
comment contradiction against diagnosis §8; (d) the 0x26 fallback
dispatches on WeenieErrorMessages' own Type instead of hardcoding
ClientLocal.
Full Release suite: 12,553 passed / 4 skipped / 0 failed (baseline
03404b71: 12,542/4/0; net +11 tests, zero regressions).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
User-gate round 3 findings (a)-(c):
(a) SpewBox: TopOffset moves from the round-1 60px placeholder to 0 (flush
to the viewport top). SpewBoxController never wired DatFont/Font at all
before this round, so it silently rendered through the 15px debug
BitmapFont fallback; it now resolves retail dat Font 0x40000025
(MaxCharHeight=11px) through a new RetailUiRuntime.Assets accessor —
the smallest font id confirmed in use by any currently-imported retail
LayoutDesc fixture, cross-referenced against every
tests/AcDream.App.Tests/UI/Layout/fixtures/*.json dump and confirmed
against the installed DAT via AcDream.Cli dump-font-atlas. It is also the
chat window's own smallest font (the 0x2100006F floating-window 1/2/3/4
indicator badges), so both selection criteria the brief offered agree.
Both remain best-available approximations, not resolved retail values —
register row AP-178 updated accordingly.
(b)/(c) /help and /help death: round 2 extracted the individual retail
strings byte-exact but never traced ClientCommunicationSystem::DoHelp's
complete print sequence. Byte-swept DoHelp's own range plus the five
Summary-branch functions it calls into (HelpEmote/HelpSquelch/
HelpStatusGroup/HelpTextGroup/HelpAllGroup) against the PDB-paired
acclient.exe. Retail's real shape: bare /help prints exactly TWO scroll
entries (HelpPrefixNote, then the 13-item AvailableHelpListing built from
DoHelp's own literals and each group's Summary_HelpType branch, in exact
source order) — not the acdream-invented cheat sheet BuildHelpText()
built before. Any resolved /help <verb> gets the SAME two-entry shape:
HelpPrefixNote, then ForMoreInformationPrefix concatenated directly onto
the verb's own Detail text (retail's own unsubstituted "<command>"
literal, ported verbatim). ChatCommandRouter.EmitVerbHelp applies this
uniformly to every resolved verb, not just death. An unresolved verb now
shows retail's real "Unknown command" fallback text; that fallback types
0x1A (ClientLocal), which retail routes to the SpewBox exclusively — a
gap ChatVM's UI.Abstractions layer can't yet reach, filed as ISSUES #367
/ register AP-186 rather than left silently unregistered.
Jump-in-air (round 2's open item 1) was root-caused and fixed separately
at a5a7eb4f between rounds — recorded in the campaign ledger.
Debug suite (all projects): 12,329 passed / 4 skipped / 1 failed — the
one failure is issue #351, a pre-existing Debug-only streaming flake
confirmed reproducing identically on the pristine pre-round-3 commit via
git stash, not a regression. Release verification covers every project
reachable without rebuilding AcDream.App: a live client process (PID
15064) held its own Release binaries locked for the session and was not
killed per project policy — AcDream.UI.Abstractions.Tests (867/867, the
layer both /help fixes live in) plus every other non-App-dependent
project, all 0 failed. AcDream.App/AcDream.App.Tests/AcDream.Core.Tests
(the SpewBox fix's layer) are green in Debug only this session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Item 2: retail's portal-space "In Portal Space..." notice is the SpewBox
(ECM_UI::SendNotice_DisplayStringInfo(0x1A,...) -> AddTextToScroll(str,
0x1A, 1, 0), hardcoded to the SpewBox per the decomp), not a dedicated
centered overlay. PortalWaitNoticeController and its lease are deleted;
PortalTunnelPresentation's per-rotation-segment cadence now writes
straight into RuntimeCommunicationState.AddText(ClientLocal) -- the
SpewBox's own dedupe-at-index-0 handles the repetition exactly as
retail's does. Register row AP-184 records the surface fix and the AP-178
scope extension.
Items 4+5: /help text was partially fabricated -- the user caught the
"/help death" meta-message. Generalized
tools/pdb-extract/sweep_weenie_strings.py to decode narrow
PStringBase<char> literals (the ClientCommunicationSystem::Help* family's
shape) alongside its original UTF-16LE support, then swept every
HelpXxxGroup function's exact byte extent against the PDB-paired
acclient.exe. 4 of 7 group topics (death/status/text/allegiances) are now
complete verbatim listings; the other 3 (channels/chatting/commands) keep
an honest UNVERIFIED note citing HelpStupidChannelHack @0x0056f290 (a
genuinely undecodable BN-mislabeled-fragment mechanism) instead of the
old fabricated sentinel. 7 of ~35 channel one-liners are also now
verbatim. ISSUES.md #364 tracks the remainder;
RetailCommandHelpTableTests.cs pins every result byte-exact.
Item 1: jump-in-air refusal still silent live is NOT reproduced and NOT
speculatively fixed. Exhaustive static re-audit found the mechanism
correct by construction (single-writer OnWalkable, exactly-once-per-frame
Update()/Capture(), no interfering edge-history resets). A live headless
repro (new jump-probe bot policy, real ACE connect) was blocked --
probeaccount2 has no character, and the graphical client already owned
testaccount this session so the task's own fallback rule forbade using
it. Two temporary probes are left behind ACDREAM_PROBE_JUMP=1 (blocked
entirely in Headless by the existing multi-session static-state guard --
graphical-only for the next round).
Item 3 confirmed fixed, no regression. Item 6 (resize: no diagonal
cursors, cannot grow Y from bottom-right) folded into CH6a's existing
scope.
Full Release suite: 12,267 passed / 4 skipped / 0 failed (up from
12,221/4/0).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>