fix(chargen): Campaign CC CC6b-MOUNT re-review residuals R1-R3 + nits — REVIEW-CLOSED

R1: LivePresentationComposition's chargen-preview diagnostic fired on every
ordinary launch without ACDREAM_RETAIL_UI set, since interaction.RetainedUi
is null in that configuration and there's no Appearance page to warn about.
Narrowed the else-if guard to require RetainedUi is not null too, so the
diagnostic only fires in the one configuration it actually diagnoses.

R2: filed AP-221 for the F8 one-shot-binding disposition the re-reviewer
accepted as scoped but which shipped without its own register row — the
chargen preview's GPU-side binding reads the retryable mount coordinator's
widget exactly once, so a slow-DAT frame permanently kills the preview for
the session with only R1's diagnostic as evidence.

R3: rewrote AP-217 after re-deriving from the decomp. The original row
claimed the GradCircle was an interactive click-to-hue picker with no
handler wired up. gmCGAppearancePage::ListenToElementMessage's dispatch
switch has no case for the GradCircle's offset at all — it isn't a click
target in retail either. DoGradDisk is a paint-only routine that blits the
gradient art tinted with the current color (or blanks it for Eyes)
whenever SetColor/SetSelection run. acdream's real gap is that it never
repaints the GradCircle — a cosmetic paint gap, not a dead control.

N1: tightened AP-220's "leaving Gearknight for something else" — the
decomp shows leaving Gearknight for Olthoi/OlthoiAcid takes a separate
branch that does not randomize; only leaving for a non-Olthoi heritage
does.

N2: added the requested media pin to the F2 spin-highlight live-DAT test,
then measured it against the installed EoR dat rather than assuming it
would pass. It doesn't: none of the nine spins author Highlight-state
media on either consumed arrow face segment, so
TrySetRetailState(Highlight) is a silent no-op for all of them today.
Pinned the test to the measured reality (ActiveState stays "Normal") and
filed AP-222 documenting the discovery — unresolved whether retail's own
spin art has the same gap.

Plan doc: CC6b-MOUNT ledger row updated to REVIEW-CLOSED with the full
commit chain and re-review disposition; OWED list corrected for AP-217/
AP-222.

Gates: dotnet build -c Release green. App suite 5223 passed / 3 skipped
(ACDREAM_PROBE_LIVE_MOUNT=1, ACDREAM_DAT_DIR set) — count held exactly at
baseline. Runtime suite 1713/0 — unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-15 22:58:19 +02:00
parent d2a71152d2
commit 6114b2dda2
4 changed files with 49 additions and 5 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1078,13 +1078,25 @@ internal sealed class LivePresentationCompositionPhase
}
});
}
else if (dispatcherLease.Resource is not null)
else if (dispatcherLease.Resource is not null && interaction.RetainedUi is not null)
{
// Fix round F8: dispatcher is available but the mount coordinator
// hadn't resolved ChargenPreviewViewportWidget by this one-shot
// pass — loud instead of silent, since the coordinator's own
// later per-frame retries cannot recover this GPU-side binding
// (see this block's own disposition comment above).
//
// Re-review R1: the retained UI arm (`interaction.RetainedUi`)
// is null in the default configuration (ACDREAM_RETAIL_UI
// unset — see InteractionRetainedUiComposition.cs's own gate on
// RuntimeOptions.RetailUi), and in that configuration there is
// no Appearance page at all. The dispatcher lease is
// acquired unconditionally regardless of retained-UI presence,
// so without this second guard every ordinary launch printed
// this diagnostic even though nothing was actually broken.
// Narrowed to fire only in the one configuration it is meant to
// diagnose: retained UI mounted, dispatcher ready, but the
// coordinator's widget resolution missed this one-shot pass.
Console.WriteLine(
"[UI] chargen preview viewport unavailable at composition "
+ "time — the Appearance page's zoom/rotate controls and "

View file

@ -370,6 +370,36 @@ public sealed class CharacterCreationLiveDatTests
// drops it shows up here instead of as a silently-dead
// highlight.
Assert.True(spin.ToggleBehavior, $"spin 0x{spinId:X8} must author ToggleBehavior for the current-part highlight to work.");
// Re-review nit N2 (2026-08-15): ToggleBehavior alone is necessary
// but not sufficient — UiButton.UpdateVisualState only COMMITS the
// requested state when _availableStates actually contains it
// (UiButton.cs's TrySetRetailState -> Selected setter ->
// UpdateVisualState chain). MEASURED (not assumed) against the
// installed EoR dat: TrySetRetailState(Highlight) itself always
// reports success (the ToggleBehavior branch commits
// unconditionally, matching TrySetRetailState's own contract),
// but NONE of the nine spins actually carries Highlight /
// Highlight_rollover / Highlight_pressed media on either of
// their two consumed arrow face segments — every one of them
// authors only Normal / Normal_rollover / Ghosted. So F2 item 2's
// current-part highlight is CURRENTLY A NO-OP for every spin:
// ActiveState silently stays at its prior value ("Normal")
// instead of ever becoming "Highlight". The pre-existing
// ToggleBehavior pin above never caught this because it only
// checks the PROPERTY that gates the state-machine branch, not
// whether that branch has anything to actually draw. Filed as
// AP-222 (retail-vs-acdream status unresolved — retail's own
// gmCGAppearancePage::SetSelection call sites are cited for
// the SetState(1)/SetState(6) calls, not for whether retail's
// OWN spin art authors Highlight media either). Pinned to
// "Normal" so a future DAT revision that adds real Highlight
// media is what makes this assertion start failing — the
// correct trigger to update it to "Highlight" instead of a
// silently-reintroduced dead highlight going unnoticed either way.
Assert.True(
spin.TrySetRetailState(UiButtonStateMachine.Highlight),
$"spin 0x{spinId:X8} must accept a Highlight state request.");
Assert.Equal("Normal", spin.ActiveState);
}
// Every color-wheel-family id resolves through EXISTING