merge: plugin text and unknown-command refusals go to the chat window (owner-directed, AD-124)

Owner 2026-09-07: plugin output and 'Unknown command' land in the chat
scroll, not the SpewBox. Plugin text = Decal/VTank-faithful; the
unknown-command re-route is recorded as AD-124 (retail types it 0x1A).
Bad-argument refusals of real retail commands stay SpewBox-only (AP-183).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-09-07 08:24:02 +02:00
commit 66160741f0
11 changed files with 311 additions and 65 deletions

View file

@ -74,30 +74,30 @@ after each deliberate `Top` write for the imported-layout element. Precedent:
`MapPageController.cs:235-249` (the same fix already landed for other
runtime-repositioned imported/programmatic elements).
## #488 — MossTank `.utl` expression block: length prefix measured before newline normalization
**Status:** OPEN — found 2026-09-07 by the final Opus re-check of Campaign VT
slice 1 Part A (`f58e997b1`), not reachable from the UI.
**Severity:** LOW (latent)
**Component:** `src/AcDream.Plugins.MossTank/MossTankLootProfileStore.cs` (`AttachMossTankExpressions` ~504-521, `ApplyMossTankExpressions` ~539-554) vs `VtankLootProfileSerializer.cs` (`WriteBlock` ~357-366, `NormalizePayload`)
**Description.** The MossTank-owned unknown block that carries each loot rule's
`Expression` text writes `expression.Length` as a length prefix and then the raw
text; `WriteBlock` normalizes the whole payload afterwards, rewriting a lone
`
## #488 — MossTank `.utl` expression block: length prefix measured before newline normalization
**Status:** OPEN — found 2026-09-07 by the final Opus re-check of Campaign VT
slice 1 Part A (`f58e997b1`), not reachable from the UI.
**Severity:** LOW (latent)
**Component:** `src/AcDream.Plugins.MossTank/MossTankLootProfileStore.cs` (`AttachMossTankExpressions` ~504-521, `ApplyMossTankExpressions` ~539-554) vs `VtankLootProfileSerializer.cs` (`WriteBlock` ~357-366, `NormalizePayload`)
**Description.** The MossTank-owned unknown block that carries each loot rule's
`Expression` text writes `expression.Length` as a length prefix and then the raw
text; `WriteBlock` normalizes the whole payload afterwards, rewriting a lone
`
`/` ` to `
`. An expression containing a bare newline therefore grows
after its prefix was measured, the reader truncates it, lands mid-text on the
next length line, fails `int.TryParse` and silently abandons every remaining
rule's expression. The loot expression control is a single-line field so the UI
cannot author one; the legacy-JSON sweep can (free-form JSON).
**Fix shape.** Normalize the expression before measuring it (or escape/refuse
newlines in the block), with a pin that writes a two-line expression and reads
it back through `VtankLootProfileSerializer.TryRead`. Companion cosmetics from
the same re-check: the unreachable `remaining` roster branch in the route and
loot sweeps, and the meta Delete notice printing the raw file name.
`. An expression containing a bare newline therefore grows
after its prefix was measured, the reader truncates it, lands mid-text on the
next length line, fails `int.TryParse` and silently abandons every remaining
rule's expression. The loot expression control is a single-line field so the UI
cannot author one; the legacy-JSON sweep can (free-form JSON).
**Fix shape.** Normalize the expression before measuring it (or escape/refuse
newlines in the block), with a pin that writes a two-line expression and reads
it back through `VtankLootProfileSerializer.TryRead`. Companion cosmetics from
the same re-check: the unreachable `remaining` roster branch in the route and
loot sweeps, and the meta Delete notice printing the raw file name.
## #487 — Radar compass tokens may be pinned by the anchor pass (candidate)
**Status:** OPEN — CANDIDATE, found 2026-09-06 by the Opus review of
@ -5922,6 +5922,13 @@ slice CH4).
## #363 — Chat refusal/usage call sites are typed ClientLocal 0x00 where retail types several 0x1A
**2026-09-07 owner-directed re-route:** the "Unknown command" refusals this
issue's closure routed to `ShowInterfaceText`/SpewBox now route to
`ShowSystemMessage`/the chat scroll instead, per explicit owner direction
that unknown commands must be visible in chat, not the SpewBox overlay.
Every OTHER site this issue named (bad-args refusals of real commands,
AP-183) is unaffected. See register row AD-124.
**Status:** CLOSED 2026-08-10. `ChatVM` gained a typed interface-text seam
(`OnInterfaceText` init hook + `ShowInterfaceText(text)`) that the App-layer
composition (`InteractionRetainedUiComposition.CreateRetainedUi`) wires to
@ -6261,6 +6268,14 @@ still missing); `src/AcDream.App/UI/Layout/LayoutImporter.cs`
## #367 — ChatCommandRouter's local-presentation fallbacks type-0x1A text still lands in the chat scroll, never the SpewBox
**2026-09-07 owner-directed re-route:** the two fallbacks this issue named
(`RetailCommandHelpTable.UnknownCommand` in `EmitVerbHelp`, and the
degenerate-prefix "Unknown command: {verb}." refusal) now call
`ShowSystemMessage(...)` again — back to the chat scroll, by explicit owner
direction that unknown commands must be visible there rather than in the
SpewBox this issue's 2026-08-10 closure moved them to. See register row
AD-124; this is a deliberate re-reversal, not a regression of this issue.
**Status:** CLOSED 2026-08-10, closed as a side effect of #363's
interface-text seam (fix shape (a) from this issue's own filing).
`ChatVM.OnInterfaceText` is exactly the hook this issue asked for; both

File diff suppressed because one or more lines are too long