fix(CT6): fix round — chrome-inclusive host clamp (BLOCKER B1) + 372px mount default
Opus dual-lens review of CT6 (ec50455a) found 1 blocker, 4 should-fix, 5
notes. All applied:
BLOCKER B1 — the shared gmPanelUI host (0x100005FE) IS retail's own
outer window frame, not a content element: its authored 310/372/310/1000
already include the 5px bevel on every side. RetailWindowFrame.Mount was
adding the NineSlice wrapper's OWN 10px chrome inset on top of that
already-chrome-inclusive source, clamping MinWidth to 320 while the
window's actual mounted outer width stayed 310 — silently below its own
minimum until RetailWindowManager.ResizeTo forcibly widened it despite
ResizeX=false. Fixed with a new
RetailWindowFrame.Options.DatConstraintSourceIsOuterFrame opt-out
(chrome inset = 0 for constraint resolution only, value stays
DAT-sourced); MountCharacter sets it true. Mounted clamp is now exactly
the host's four raw values: width fixed 310, height 372..1000. Added a
mount-time invariant (throws if the mounted outer extent falls outside
its own just-computed clamp) that would have caught this at the first
test run.
S4 (campaign-lead ruling) — the window must MOUNT at retail's authored
default, outer 372 (content 362, matching the host's own content parent
0x10000180), not 0x2100002E's own 300x600 content-authoring canvas
(which produced a stale 610px default pre-fix: 600 + 10 chrome inset).
372 is exactly the host's own authored MinHeight — retail opens at its
resize floor and can only be dragged taller. MountCharacter now sets
ContentHeight=362f explicitly. At this default the 9 attribute/vital
rows (180px) overflow the 160px list immediately — retail-correct, not
a regression.
S2 — 0x1000023E and 0x10000533 both author property 0x79
(HideWhenDisabled) TRUE (fixture-verified: BoolValue=true on both). A
fitting list HIDES the scrollbar entirely; it does not draw a full-track
"disabled" thumb. The code was already correct; four wrong descriptions
(plan ledger, CharacterStatController comment, CT7 script, test comment)
are corrected, plus a new IsPresentationVisible assertion pair in the
resize test.
S3 — CharacterTitlesController's `if (listBox.LayoutPolicy is null)`
Anchors fallback was unreachable on both the real DAT and the fixture
(0x10000532/0x10000539 both author HasOriginalParentSize=true, so
LayoutPolicy is always assigned). Deleted; added an InstalledDatFact pin
guarding the deletion against DAT drift.
N4 — renamed NineSlice_ChatShapedConstraints_... to
NineSlice_ContentShapedConstraints_InsetArithmeticClampsProgrammaticResize
(it tested inset arithmetic on a content-shaped source, not chat's real
contract) and added a true chat-contract pin mounting Chrome=Imported
with chat's real 300/100/2000/2000 constraints, asserting no inset
applies.
N5 — corrected the "nothing inferred, no register row" sentences in the
ground-truth doc and plan ledger: they were false pre-fix (the mounted
clamp WAS an inferred double-counted composition); true now that B1
removes the composition.
CT7 script §4 rewritten with exact clamps (no "≈"), the corrected
default-overflow scrollbar behavior, and an absolute starting-height
statement.
Verified: full hermetic solution suite green (15,441 tests, Release,
Lane exclusions per the release gate), InstalledDat lane green across
the whole solution (414 tests, ACDREAM_RUN_INSTALLED_DAT_TESTS=1,
Status!=KnownFailure) including two new pins
(TitlesListAndPage_AuthorHasOriginalParentSize,
Imported_ChatContract_ClampsAtAuthoredBoundsWithNoChromeInset).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
ec50455a63
commit
996cd73675
11 changed files with 552 additions and 120 deletions
|
|
@ -412,21 +412,74 @@ MaxHeight=1000; its bottom Resizebar (`0x10000660`) and top Dragbar
|
|||
`GetAttribute_Int(this, 0x3C..0x3F)` reads off that same parent. The
|
||||
Character/Skills slot `0x1000018E` itself authors no constraints of its
|
||||
own (confirmed, same pin). `RetailUiRuntime.MountCharacter` now imports
|
||||
that host element and passes it as `DatConstraintSource`; the mounted
|
||||
outer frame clamps at MinWidth=MaxWidth≈320, MinHeight≈382,
|
||||
MaxHeight≈1010 after the NineSlice chrome inset. Full derivation +
|
||||
decomp anchors: `docs/research/2026-08-24-campaign-ct-dat-ground-truth.md`
|
||||
§CT6.
|
||||
that host element and passes it as `DatConstraintSource`. **CORRECTED
|
||||
(CT6 fix round, BLOCKER B1):** this paragraph originally claimed the
|
||||
mounted outer frame clamped at "MinWidth=MaxWidth≈320, MinHeight≈382,
|
||||
MaxHeight≈1010 after the NineSlice chrome inset" — that was WRONG. Host
|
||||
`0x100005FE` is not a content element our wrapper adds chrome to; it IS
|
||||
retail's own outer window frame (5px bevel + 300×362 content parent
|
||||
`0x10000180` + 5px = 310×372), so its authored 0x3C..0x3F values are
|
||||
already chrome-INCLUSIVE. Adding the NineSlice wrapper's own 10px inset
|
||||
on top double-counted the bevel, clamping MinWidth to 320 while the
|
||||
window's actual mounted outer width stayed 310 — silently below its own
|
||||
minimum until `RetailWindowManager.ResizeTo` forcibly widened it despite
|
||||
`ResizeX=false`. Fixed with a new
|
||||
`RetailWindowFrame.Options.DatConstraintSourceIsOuterFrame` opt-out
|
||||
(chrome inset = 0 for constraint resolution when set — the value stays
|
||||
DAT-sourced, only the redundant inset is skipped); `MountCharacter` sets
|
||||
it true. The mounted outer clamps are now EXACTLY what the host authors:
|
||||
width fixed **310**, height **372..1000** — no composed arithmetic. A
|
||||
new mount-time invariant in `RetailWindowFrame.Mount` (throws if the
|
||||
just-mounted outer extent falls outside its own just-computed clamp)
|
||||
would have caught B1 at the very first test run; it is now permanent for
|
||||
every window this path mounts.
|
||||
|
||||
**S4 (2026-08-25, campaign-lead ruling — which number governs the
|
||||
default mount size):** `0x2100002E`'s own root is authored 300×600 (the
|
||||
"Size tension" the earlier ground-truth doc left unresolved — CT3's
|
||||
Titles page alone is 300×575, plus the 25px tab bar). That 600 is a real
|
||||
authored canvas, but it is the CONTENT's own design surface, not the
|
||||
mounted default — retail scroll-clips it into the shared host's much
|
||||
smaller 300×362 content parent (`0x10000180`). Pre-fix, `MountCharacter`
|
||||
left `ContentHeight` unset, so it fell back to the raw 600px canvas,
|
||||
producing a stale 610px mounted default (600 + 10px chrome inset) that
|
||||
was never retail's actual opening size. **372 (the host's own outer
|
||||
frame, 362 content + 10 chrome) is the number that governs the mount
|
||||
default** — it is also exactly the host's own authored MinHeight, so
|
||||
retail's Character/Skills window opens AT its resize floor and can only
|
||||
be dragged taller, never shorter. `MountCharacter` now sets
|
||||
`Options.ContentHeight = 362f` explicitly to realize this. The authored
|
||||
page composition (header 112 + list 160 + divider + footer) IS the
|
||||
362px design; at that default the 9 attribute/vital rows (180px content)
|
||||
OVERFLOW the 160px list, so the stat list's scrollbar is active
|
||||
immediately on open — retail-correct, not a regression (see S2 below for
|
||||
what "active" actually looks like). Persistence still restores a
|
||||
user-chosen size within the 372..1000 clamp on top of this default. Full
|
||||
derivation + decomp anchors:
|
||||
`docs/research/2026-08-24-campaign-ct-dat-ground-truth.md` §CT6.
|
||||
`CharacterStatController.RebuildActiveList` now wraps BOTH the
|
||||
Attributes and Skills tabs' rows in the same `UiScrollablePanel`
|
||||
viewport (previously only Skills got one; Attributes rows were added
|
||||
directly to the ListBox with no clipping/scrolling and the shared
|
||||
scrollbar was force-hidden — the owner's item 2). The shared scrollbar
|
||||
is now always bound + visible; `UiScrollbar`'s own
|
||||
`IsPresentationVisible`/`IsModelDisabled` already draw the correct
|
||||
full-track "disabled" thumb when content fits (`HideWhenDisabled`
|
||||
defaults false), so no per-tab visibility toggle is needed any more.
|
||||
This surfaced and fixed a real, previously-unexercised `#372`/
|
||||
is now always BOUND (`.Model`/`.Visible = true`); no per-tab visibility
|
||||
toggle is needed. **CORRECTED (CT6 fix round, S2):** this paragraph
|
||||
originally claimed `UiScrollbar`'s own `IsPresentationVisible`/
|
||||
`IsModelDisabled` "draw the correct full-track 'disabled' thumb when
|
||||
content fits (`HideWhenDisabled` defaults false)" — that had the
|
||||
authored default BACKWARDS. `0x1000023E` (this scrollbar) and
|
||||
`0x10000533` (the Titles list's own scrollbar) both author property
|
||||
`0x79` (`HideWhenDisabled`) **TRUE**, fixture-verified (`BoolValue: true`
|
||||
on both elements' property 121/0x79 in the committed fixture). A fitting
|
||||
list HIDES the bar entirely; it does not leave a full-track disabled
|
||||
thumb visible. The code was already correct — `.Visible = true` only
|
||||
keeps the bar in the tree, `IsPresentationVisible` does the actual
|
||||
show/hide — only this description was wrong; fixed here, in
|
||||
`CharacterStatController.RebuildActiveList`'s own comment, in the CT7
|
||||
script, and in `CharacterStatControllerTests`' comment, plus a new
|
||||
`IsPresentationVisible` assertion pair added to the resize test (hidden
|
||||
once growing makes the content fit, visible+interactive while
|
||||
overflowing). This surfaced and fixed a real, previously-unexercised `#372`/
|
||||
`#412`-class anchor-baseline bug: the viewport's `Left|Top|Bottom`
|
||||
anchor was capturing its baseline margins lazily on its OWN first
|
||||
`ApplyAnchor` call, which happens AFTER the ListBox has already grown
|
||||
|
|
@ -435,13 +488,19 @@ bogus non-zero margin that permanently capped the viewport short on
|
|||
every later resize. Fixed with an eager
|
||||
`viewport.CaptureCurrentAnchorBaseline()` call right after
|
||||
`AddChild`, mirroring the identical fix already shipped in
|
||||
`UiTemplateListBox.Viewport`'s own lazy getter. `CharacterTitlesController.Bind`
|
||||
gained the same defensive `Anchors = Left|Top|Bottom` fallback for the
|
||||
Titles ListBox (`0x10000532`) that `CharacterStatController` already
|
||||
had for its own list — live-verified as a no-op on the real DAT (both
|
||||
the Titles page container and its ListBox already carry a real authored
|
||||
`LayoutPolicy` that stretches correctly on its own), but matching the
|
||||
established pattern for synthetic/test layouts.
|
||||
`UiTemplateListBox.Viewport`'s own lazy getter. **CORRECTED (CT6 fix
|
||||
round, S3):** `CharacterTitlesController.Bind` originally gained the
|
||||
same defensive `if (listBox.LayoutPolicy is null) Anchors =
|
||||
Left|Top|Bottom` fallback for the Titles ListBox (`0x10000532`) that
|
||||
`CharacterStatController` already had for its own list. Both
|
||||
`0x10000532` and the Titles page container `0x10000539` author
|
||||
`HasOriginalParentSize=true` in the real DAT AND the committed fixture,
|
||||
which makes `LayoutImporter`/`DatWidgetFactory` always assign a real
|
||||
`LayoutPolicy` — the fallback branch was therefore UNREACHABLE, not a
|
||||
harmless no-op "matching the established pattern for synthetic/test
|
||||
layouts" as originally described. Deleted rather than left as dead code;
|
||||
a new `CharacterPanelLiveDatTests` pin asserts `HasOriginalParentSize`
|
||||
on both elements to guard the deletion against future DAT drift.
|
||||
STANDARDIZATION AUDIT (no gaps found, no follow-up filed): `UiElement
|
||||
.MinWidth/MinHeight/MaxWidth/MaxHeight`, set once at
|
||||
`RetailWindowFrame.Mount` from `Options.DatConstraintSource`/explicit
|
||||
|
|
@ -451,10 +510,20 @@ path (`RetailWindowManager.ResizeTo`, which both `RetailPanelUiController`'s
|
|||
main-panel geometry sync and this slice's tests exercise), and the
|
||||
persisted-geometry restore clamp (`RetailWindowLayoutPersistence.Apply`).
|
||||
`RetailWindowFrame.Mount` remains the single production mount path (no
|
||||
window bypasses it). New regression pin:
|
||||
`RetailWindowFrameTests.NineSlice_ChatShapedConstraints_ClampProgrammaticResizeAtAuthoredBounds`
|
||||
window bypasses it). New regression pin (**renamed, CT6 fix round N4**:
|
||||
the original name `NineSlice_ChatShapedConstraints_
|
||||
ClampProgrammaticResizeAtAuthoredBounds` overclaimed — it exercises
|
||||
NineSlice inset ARITHMETIC on a content-shaped source (490×100,
|
||||
height-only synthetic constraints) and never actually pinned chat's real
|
||||
DAT contract, since no width constraints were even set):
|
||||
`RetailWindowFrameTests.NineSlice_ContentShapedConstraints_InsetArithmeticClampsProgrammaticResize`
|
||||
proves the same mechanism still clamps chat-shaped constraints after
|
||||
Character was wired onto it. Tests: `CharacterStatControllerTests
|
||||
Character was wired onto it. A new companion test,
|
||||
`Imported_ChatContract_ClampsAtAuthoredBoundsWithNoChromeInset`, mounts
|
||||
with `Chrome=Imported` and chat's real 300/100/2000/2000 constraints
|
||||
(matching production's actual `MountChat` wiring) and asserts no inset
|
||||
applies — the true chat-contract pin the renamed test's name no longer
|
||||
claims to be. Tests: `CharacterStatControllerTests
|
||||
.CharacterWindow_ResizesYWithinAuthoredHostClamp_AndReflowsListAndScrollbar`
|
||||
(window-level: clamp at authored min/max, list shrink, scrollbar
|
||||
overflow flip, footer stays bottom-docked, grow-back restore) and
|
||||
|
|
@ -462,9 +531,16 @@ overflow flip, footer stays bottom-docked, grow-back restore) and
|
|||
(same contract for the Titles list) plus the pre-existing 126+22-test
|
||||
suites, all updated where the new nested-viewport DOM shape required it
|
||||
(`Descendants(list)` instead of `list.Children` — the shape Skills rows
|
||||
already needed). No register row: every number is either a live-probed
|
||||
authored DAT value or a structural anchor-capture-correctness fix,
|
||||
nothing inferred.
|
||||
already needed). No register row: **CORRECTED (CT6 fix round, N5)** —
|
||||
before the B1 fix this sentence ("every number is either a live-probed
|
||||
authored DAT value ... nothing inferred") was not actually true: the
|
||||
mounted 320/382/1010 clamp WAS an inference (the host's chrome-inclusive
|
||||
values plus a second, redundant chrome inset composed on top). After B1
|
||||
removes that composition, the mounted clamp is now literally the host's
|
||||
own four probed values with zero arithmetic applied — the sentence holds
|
||||
for real. No register row for the S4 content-height default either: 362
|
||||
is the same host content-parent width/height CT6 already probed and
|
||||
cited (`0x10000180`, 300×362), not a new number.
|
||||
|
||||
**CT7 — Connected gate.** Test script
|
||||
(`docs/research/2026-08-25-campaign-ct-test-script.md`), owner drive:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue