acdream/tests/AcDream.Core.Tests/Chat
Erik 2621fbf4a7 feat(chat): CT-A2 — parse retail's inline chat tag markup
Campaign CT slice A2. Pure parser, no UI, nothing wired yet.

Retail's client composes chat lines with the markup already embedded —
Handle_Communication__HearSpeech @0x005712A0 sprintf's it — and the text
element recognises it while appending (UIElement_Text::InqGlyphs @0x00468EA0),
calling TextTagFactory::MakeTag @0x00478480 per marker. A tagged speaker name
arrives as:

    <Tell:IIDString:1342177290:Dww>Dww<\Tell> tells you, "hello"

ChatTagMarkup.Parse splits that into spans, consuming the markers: the name
under a tag, the remainder untagged.

The rule that decides where a tag ENDS is the one worth being careful about.
It is the absence of a colon, not the backslash: MakeTag requires a ':' to
succeed, so ANY bracketed text it cannot parse closes the open tag, and the
backslash in retail's own closer (TextTag::BuildEndTag @0x00479190) is
incidental to that. Porting "a closer starts with a backslash" would look
correct on every retail line and then diverge on everything else, so the test
pins all three of <\Tell>, <Tell> and <anything> as closers.

Two details taken from the decomp rather than guessed: only the FIRST colon of
an IIDString payload separates the id from the name, so a name containing a
colon survives intact (ParseStartTag @0x00478910); and an unterminated '<' is
ordinary text, so a player typing "is 3 < 4 really" does not lose the rest of
their sentence.

The parse also upholds the contract CT-A1's draw side enforces — the
concatenated span text always reproduces the visible line, because selection
and hit-testing index into that flat string.

Solution builds clean; full hermetic gate green.

Note for the record: PreparedAssetVerificationCacheTests.BackupRecoveryHashes-
TheBackupEvenWhenTheLiveCacheIsValid failed once during this slice's gate and
then passed isolated, as a class, and on a full-gate rerun. This branch touches
no launcher code, so it is load-sensitive rather than caused here — flagging it
rather than silently re-running, since a test that only fails under parallel
load is worth someone classifying.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 07:23:07 +02:00
..
ChatChannelInfoTests.cs fix(chat): CH3 review fixes — phantom UN-9, allegiance-broadcast echo, /a legacy fallback 2026-08-09 20:24:29 +02:00
ChatCommandTargetStateTests.cs fix(chat): CH1 review fixes — sbb-idiom channel catch-all, command-output typing 2026-08-09 16:03:13 +02:00
ChatLogLocalGuidTests.cs fix(chat): CH1 review fixes — sbb-idiom channel catch-all, command-output typing 2026-08-09 16:03:13 +02:00
ChatLogSystemDedupTests.cs fix(chat): /help client-side handler + System dedup + ThatIsNotAValidCommand template 2026-04-25 21:22:07 +02:00
ChatLogTests.cs fix(ui,runtime): OP4 re-review residuals R1-R4 (coordinator pass) — OP4 CLOSED 2026-08-11 06:36:14 +02:00
ChatTagMarkupTests.cs feat(chat): CT-A2 — parse retail's inline chat tag markup 2026-08-21 07:23:07 +02:00
ChatWindowStateTests.cs feat(ui): Campaign OP slice OP5 — the Chat tab 2026-08-11 06:25:59 +02:00
CombatChatTranslatorTests.cs feat(chat): Campaign CH slice CH1 — retail LogTextType color table 2026-08-09 15:24:09 +02:00
InventoryFailureMessagesTests.cs fix: social gate round 2, part 2 - confirmation-dialog sentences + the 2026-08-13 21:10:42 +02:00
LegacyChannelChatTypeTests.cs fix(chat): CH1 review fixes — sbb-idiom channel catch-all, command-output typing 2026-08-09 16:03:13 +02:00
SpewBoxStateTests.cs fix(chat): CH2 rework — SpewBox tick-driven visibility + binary-derived error table 2026-08-09 18:14:26 +02:00
TurbineChatStateTests.cs refactor(net): converge live session state reset 2026-07-21 12:00:48 +02:00
WeenieErrorMessagesTests.cs fix(chat): FA1 -- 8 fellowship WeenieError ids confirmed absent from retail, not missing 2026-08-12 00:13:25 +02:00