Every production registration now hands ShadowObjectRegistry the object's
whole visual part array beside its collision dispatch: static publication
(App LandblockPhysicsPublisher and the headless Content twin) through
ShadowShapeBuilder.FromStaticRenderParts, live entities (Runtime
LiveEntityCollisionBuilder) through the new FromSetupRenderParts, which walks
every Setup part with the same physics-sphere-else-drawing-sphere and
part-box rule from the PhysicsDataCache Runtime already reaches. Nothing
consumes the retail products yet; the App hermetic lane still passes
6,758/6,758.
The Lane=InstalledDat comparator registers five real fixtures through the
real publication inputs and prints retail CELLARRAY, collision cells, and
the old render cells side by side: Facility Hub stair Setup 0x02000623
(7 cells incl. 0x8A02015F/015E), cathedral ramp 0x020009A2 (3 cells, the
genuine multi-part case), the #334 Neftet formation (25 cells), and a
landblock-edge crosser (6 cells, 2 in the neighbor block). All three
answers agree for BSP-bearing objects, as the shared primitive predicts;
the divergence chunk 3 expects appears only for decorative non-BSP parts.
Core Physics 2,202/2,202; Runtime 1,884/1,884; App hermetic 6,758/6,758;
comparator + stair pin 5/5; solution Release build 0 warnings / 0 errors.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Read-only trace of every production owner of cell membership (render and
physics), the retail transaction they must collapse into (Contracts A/B),
and the five S2 chunks re-cut on that evidence: the two static floods are
one CellTransit walk with different part inputs; retail uses the whole
part array once and registers every part in every array cell; children
inherit at registration; topology consolidation moves to S3.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Campaign OVERHAUL S1 review fixes (two Opus lens reviews, findings verified
by the lead against the decomp):
- a raw sides_type outside 0/1/2 constructs retail's default single-side
shape (ConstructMesh @0x0059DFA0 loop bounds default to 1) instead of
dropping the polygon; still counted as a data anomaly (corpus has none);
- the positive-surface stippling mask OR runs once per polygon before the
degenerate-fan guard, as retail's count loop does (pseudo-C 426859-426866);
- untextured slots keep their mask accounting but bake no texture and no
vertices (contract §9 item 3); the dev pak shrinks by 114 KB;
- failed surface-override / Surface / texture-dependency lookups are
attempted and logged once per slot, not once per candidate;
- cell-shell batches upload in ascending source surface index, retail's
built-EnvCell subset draw order (ConstructMesh attribute-range scan,
DrawMesh @0x0059D4A0); ordinary GfxObj meshes keep storage order;
- CellMesh.HasDrawableGeometry documented as the admission rule without
texture-dependency resolution (a conservative superset of emission);
- the stippling/surface equivalence sweep's cell half is pinned at zero
again; InAscendingSurfaceOrder is marked bake/upload/test-only;
- plan §5: reviewer findings are verified by the lead, one skeptic at most
for a blocking finding, never more than five agents per step.
Three new Content tests pin the mask hoist, the vertex-free untextured
slot, and the single-side fallback. Content 213/213, Core Meshing and
Conformance green, App hermetic 6,757/6,757, Release build 0/0.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Campaign OVERHAUL S1 chunk B.
- CellMesh.Build's render role is deleted; CellMesh.HasDrawableGeometry
is the exact predicate (any sides_type candidate whose resolved surface
passes the built-EnvCell (Surface.Type & 6) != 0 test), and the
streaming build job consumes it. Core and Content no longer carry two
CellStruct interpretations.
- Installed-DAT scan over the complete Environment corpus (5,346
landblocks, 729,888 cells, 8,601,560 polygons, 8,608,746 candidates):
3,197 old-rule admissions were untextured surfaces the NoPos proxy let
through; zero new-only; zero unexplained; zero unknown sides values;
counts pinned as goldens against the contract's DAT hashes.
- Canonical pins: 0xF4180104 has eight ST_DOUBLE clip-map polygons and 44
drawable side calls; the canonical NoPos surfaces are type 0x11 and are
constructed but skipped; two independent extractions of all ten
canonical cells hash identically.
- AP-234 retired from the divergence register (161 active rows) with
ConstructMesh 0x0059DFA0 / DrawEnvCell 0x0059F170 / DrawMesh 0x0059D4A0.
- StipplingSurfaceEquivalenceTests no longer claims a build-time NoPos
skip; the cell half is report-only, the building half stays pinned.
- Inventory and policy remarks updated; plan ledger records chunk A and
the capture kit.
Core Meshing+Conformance 95/95, Content 208/208 (Lane!=Timing,
Lane!=PreparedPackage), App EnvCell/LandblockBuild/Streaming 432/432,
Release solution build 0 warnings / 0 errors.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
v2 replaces the twelve-stage plan. OH1's evidence-grammar stage is parked
on quarantine/oh1-evidence-grammar-2026-09-02; the committed research
contracts stay binding. Stage mapping: OH2 -> S1, OH3 -> S2, OH4+OH5 -> S3,
OH6+OH7 -> S4, OH8-OH11 -> S5. Adds the working model (lead in the loop,
bounded chunks, time-box), the single retail capture session before S3,
and per-slice minimal evidence products. Carries the InitCell inflag
prose correction in the flood appendix and marks the T3 handoff superseded.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The cathedral falls shine-through (owner report at the #456 terrace
seam, toggling with the camera''s side of the seam): the probe''s new
phase tags pinned the falls emitters to the cell-owners route
submitting POST-depth-clear on interior roots - retail''s own timing,
safe only because seals own the aperture depth by the end-of-frame
alpha drain. Our seal SET still came from the old apparatus''s flood,
which misses exit portals at the #456 seam band (its never-drawn panel
family), so the drain z-passed across the unsealed opening.
DrawWalkExitPortalMasks now stamps the walk''s own ordered interior
flood (WalkFrameDriver.InteriorFloodCells, captured at
OnInteriorFloodDrawTurn - retail PView::DrawCells pc:432785-432786),
keeping the old assembly''s per-cell slice clips where present and
sealing unclipped where the old apparatus had no entry (benign: the
exact dat aperture polygon, z-tested). The legacy path keeps the old
seal draw. Also: ACDREAM_PROBE_WALK_ROOT gains [walk-part]/[walk-alpha]
phase-tagged submission/drain lines (the apparatus that pinned this).
Hermetic 6,762/0, Walk lane 213/1, InstalledDat walk conformance 40/1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The FW3 visual gate's stairwell/grass transition flash (grass briefly
covering floor openings at doorway crossings - the #119 family) was the
FW3 dual path leaking: the walk decided WHETHER terrain draws while the
old PortalVisibilityBuilder assembly decided WHERE (slice planes, count,
scissor), and punch fans indexed the old slice array with walk view
indices. The new ACDREAM_PROBE_WALK_ROOT apparatus pinned the boundary
frames: fat/degenerate old-apparatus exit views splash terrain over
interior pixels, the interior depth-clear preserves color, and cells
absent from the walk's flood never repaint. Retail has ONE visibility
structure and cannot produce this.
ClipFrameAssembler.ReassembleOutsideViewFromWalk now materializes the
walk's own outside_view (pixel screen points -> standard NDC -> the
existing ClipPlaneSet machinery) into the assembly's outside-view block
after Collect, ahead of the single PrepareClipFrame publication (moved
below the walk block). The Landscape event carries the walk's active
view count on the record's existing OutsideViewCount field (trace
mapping compares kind only - zero oracle-fixture churn) and the driver
fans exactly that many terrain slices; activeTerrainSliceCount is
deleted end to end. Outdoor roots keep the assembler's single
full-screen slice, asserted ==1.
Hermetic 6,762/0 (4 new materializer tests pin the y-flip and
plane-sign conventions), Walk lane 209/1, InstalledDat walk conformance
40/1. Seals/cell slices/look-in seeding stay on the old per-cell views
for the rest of FW4 (identical dat polygons; only the visible set can
differ).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The retail frame walk now drives every production static draw. In
RetailPViewRenderer.DrawInside, when the concrete executor + the
packed product + the FW3.1 walk registries are all wired (all
production compositions - anything less throws):
- A pre-walk events-only collection pass (the shadow sink generalized
to WalkVisitedSetCollector) gathers the frame's visited cells,
buildings, and landscape-cell turns; the visited cells union into
prepareCells so EnvCellRenderer prepares every shell the driver
draws.
- DrawWalkDrivenStatics runs the WalkFrameDriver over the production
world data (WalkProductionWorldData over RenderSceneQuery + the
building registry): sky, terrain slices, outdoor statics at their
landscape-cell turns, buildings (alpha barrier -> punch/look-in
passes -> shell) in retail order, interior clear+seals as leaf
closures (the old tail block's drain reasoning moves with them),
flood cells shell-then-contents. Landscape/cell-stage particle
owners re-source from the walk's visited sets - retail gates
particles per cell turn (ShouldDrawParticles @0x0050FE60), which
this is; the old sphere filter was the approximation.
- DrawLandscapeDynamicsPhase + DrawBuildingLookInDynamics carry the
dynamics-only remainder (LookInObject now dynamic-classified,
late outside-dynamics + weather, particle unions); DrawDynamicsLast
and the outdoor flush are unchanged.
- The product builder stops emitting LandscapeOutdoorStatic /
LandscapeBuildingShell / CellStatic (methods deleted, dead index
tracking removed); LookInObject loads cells with
includeStatics: false.
The old static path survives ONLY behind !walkActive for the
standalone/diagnostic executor-fake path that keeps 15 retail-ordering
regression tests exercising the barrier/punch/seal machinery; no
production composition can reach it. Its deletion is FW4 scope (the
plan's "deleting the patch apparatus") - recorded in the plan.
Transitional risks recorded in code/report: the two-pass walk cost
(FW3.4 measures), the interior slice-count reconciliation between the
old clip assembly and the walk's own exit-view survival, and the
outdoor merged-flood punch coverage now riding the walk's own
building-BSP punches (retail-faithful per FW1; the owner visual gate
verifies).
Suites (lead-verified): full Release build 0 warnings; hermetic
6,750/0 (baseline minus the three deleted route tests); Walk lane
201/1; InstalledDat Walk conformance 40/1 untouched. The two
IL-branch tests the implementation round reported failing pass in
every lead run - the recurring parallel-load flake pair.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FW2 gate adjudication recorded (RecordingGpuDevice referees are the
automated gate; the pixel A/B rides FW3's cutover toggle). FW3 sliced
from the verified surface survey: FW3.1 production walk world data
(the Building record drops every walk field today - the biggest gap),
FW3.2 walk-driven static routes (stop EMITTING the three static routes;
ClipFrameAssembler/ViewconeCuller survive for the dynamic routes;
walk-path selection publication - picking is an unlisted consumer of
the static draw path), FW3.3 ShellDrawLiftZ retirement (THREE sites -
AP-32 lists only two), FW3.4 perf checkpoint with the baseline
correction (519.7 fps is the Aerlinthe ordinary profile, not dense
Arwic - capture a fresh pre-cutover dense-Arwic pair first).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The walk-order submission layer over the existing RHI (plan section FW2):
- OrderedDrawStream: append-only walk-ordered draw commands
(GroupKey + transform + per-instance data + WalkDrawStage + cell
provenance), struct-of-arrays with one lockstep Reset (#193 shape).
The PortalPunch stage exists but has no FW2 submission path - the
submitter throws on it; punch emission lands with FW3 wiring.
- WbDrawDispatcher.OrderedStream partial: per-instance-first emission
(the deferred-alpha shape - command i owns instance i, walk order
survives into the indirect array), each SSBO section written once,
then one DrawIndirectRangeRhi call per maximal merge run. Runs are
built by pure-CPU BuildOrderedMergeRuns and may never span a stage,
pipeline-bucket, or cull boundary; ValidateMergeRun re-checks every
emitted run and throws (the campaign fail-loud rule). Nothing is
sorted, reordered, or dropped: N commands in, N indirect commands
out, covered exactly once.
- WorldDepthContract: retail world depth verified verbatim from the
decomp - Render::zfuncVal @0x00820e1c = 0x2, SetDepthBufferMode
@0x005a2d10 writes the enum directly as D3DRS_ZFUNC so the value IS
D3DCMP_LESS, applied by the surface-state applier @0x0059c80a with
Z-write toggled by blend; the LESSEQUAL sites are GameSky::Draw-local.
Seven world pipeline sites now cite the named constant (no value
changes).
- Plan updated: FW1 status block + gate amendment (the ten pose-stamped
retail traces supersede re-expressing the old-builder replay
fixtures; those retire with the old builder at FW4 and their
scenario classes re-verify at the FW3/FW4 connected gates).
Known FW2 scope notes recorded in the code: the building-detail
overlay replay is production wiring (FW3); the _drawCullModes scratch
may not interleave with a mid-flight RetailAlphaQueue scope (FW3
sequencing constraint). The pixel A/B equivalence proof rides FW3's
cutover toggle where a walk-driven scene first exists.
Suites: full Release build 0 warnings; Walk lane 154/1 skip;
hermetic 6,714/0 (+27 new).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FW0 ledger entry records the fixtures, replay helper, pseudocode model,
and the reshaping findings. The pose gap (first captures lack camera
pose; exact replay needs it) is documented with a ready template for a
short owner session when FW1 gates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stages FW0-FW6: walk oracle fixtures -> pure-CPU RetailFrameWalk/PViewSet
-> order-preserving submitter -> static cutover with the perf stop-rule
-> entity/particle/dynamics cutover deleting the patch apparatus ->
retail camera envelope -> closeout. The plan carries the recon-verified
seam map (frame chain, replace-vs-reuse split in the dispatcher, the
complete visibility-consumer list, the streaming atomicity facts, exact
patch-apparatus sites) and the binding rules (fail loud, transition
checklist, no dual path). Campaign renamed W->FW: the roadmap already
owns Phase W (UCG).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Harden keyboard and camera routing, inventory and vendor interactions, chat/emotes, relog portal flow, and paperdoll rendering. Add retail research, connected gate coverage, and release-gate validation.
The owner ran the Campaign AS connected gate live and passed it. The two
gate findings resolved in-round: the extras-list "black rectangle" is
retail's own authored scroll-less clipped listbox (no scrollbar authored
on 0x10000335, verified against the live DAT; wheel-scroll/resize reveal
rows — AS-GF1 65f6f584 ruled it not a code defect), and the paperdoll
symptom narrowed from "renders nothing" to an intermittent FIRST-OPEN
DELAY: the probe round proved the private render layer healthy from the
first frames (nonzero handle, 34 MeshRefs, sane bounds/camera) for both
the examination clone and the inventory doll, with mesh residency/upload
latency the leading suspect. #443 stays open with that narrowed shape.
Per the probe-dies-with-its-investigation rule this strips
CreatureAppraisalViewportDiagnostics, its call sites, and the
launch-options row in one commit (recoverable via git show 65f6f584).
App hermetic suite green (6,337/0).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AS1-AS5 are all review-closed (AS5: 170/170 title strings verified, 6 of
them PE byte-decoded; zero behavioral findings across the campaign after
AS2). The AS6 connected-gate script is written and waits on the owner:
docs/research/2026-08-25-campaign-as-test-script.md, carrying the two
standing rulings (Society row colors are model-only pending AP-110
FontInfo — do not gate on them; the AD-114 animated paperdoll deviation
is expected) and the R3 unconditional-legend retail side-by-side check.
Also: the re-review's grep-hygiene tail — the last "retires AP-109"
phrasing in AppraisalUiController.cs now reads "closes AP-109's title-bar
residual", and the CT plan's stale "this campaign retires AP-109" intent
line (never executed — CT4 narrowed) carries a dated correction.
Branch remains unpushed per the owner's standing instruction.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The AS5 review verified the port exhaustively (170/170 strings, 20
dispatch arms, 17 bounds tests, both call sites) and found one docs-class
defect: five code comments plus the plan slice text claimed AP-109 was
RETIRED while the register correctly keeps the row ACTIVE-narrowed (CT4
FormatXp GetNumberFormatA approximation sliver survives). Comment-only
edits; compile-checked; no behavior change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The AS4 dual-lens review approved the port as exact and required a
docs-only fix round:
- Ground truth §2b row 1: the Society gate is PRESENCE of Int 281
(AppraisalProfile::InqInt @0x005B3830 returns found/not-found), not
value!=0; the color rule is same-bit-first (@0x004b49fd/@0x004b4a49/
@0x004b4a8b) so a multi-bit local player still resolves green on a
match; the ??? arm precedes the Radiant Blood test.
- Ruling R5 corrected: the row model carries CreatureAppraisalValueStyle
but ResolveColor is a deliberate no-op until AP-110's FontInfo-list
residual lands — the Society green/red is model-only and invisible at
the connected gate; AS6's script must not gate on row colors.
- Ledger: AS4 land 4ade9b04; true full-solution hermetic count is 15,528
(the AS4 commit body's 15,410 was a mis-report; the review re-ran and
reconciled 15,483 + 45 new = 15,528).
- #442 filed: pre-existing parallel-load flake in the shadow-caster
zero-allocation pin, surfaced by the review's full-solution run;
isolation evidence recorded; unrelated to Campaign AS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The AS3 dual-lens review verified the armor-level trio, sentinel, legend,
and monster-path exclusion at offset level and disproved the R3
BN-flattening theory structurally (legend sits outside the InqCreature
block, pseudo-C line 189962). The ratings adjudication resolved in the
implementer's favor: the pre-AS3 composer already had retail's per-row
gating and spacer discipline. NITs 11/12 + the AP-110 narrowing ride AS4.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-review of cc5290af accepted all six findings; the retail port is exact.
The paperdoll ruling bullet now reads cleanly (the retail-colors clause was
dangling off the decomp citation) and points at AD-114. The three remaining
Core-side PK-bit copies are flagged as a follow-up chip outside AS scope.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Opus's dual-lens review of the AS2 header-identity-block commit
(f8a22589) approved the retail port as exact and ordered six
mechanical fixes:
- F13: correct the plan doc's "static tinted preview" claim about
retail's examine-window paperdoll — the decomp shows retail's clone
IS animated (BasicCreatureExamineUI::Init @0x004AB9C0 ->
CPhysicsObj::makeObject -> MorphToExistingObject +
play_script_internal, CreatureMode::Render driving update_position
every frame), just decoupled from the live target. AD-114 already
records the correct deviation; only the plan doc's rationale text
was wrong.
- F14: add a regression test pinning the monster path (character:
false, success:false, no String 5 / Int 0x105 marker) so element
0x1000053A stays cleared — the exact case that used to emit the
invented "Assessment incomplete" literal AS2 deleted. This is the
test coverage the AS2 commit message over-claimed already existed.
- F15: dedup the PK/PKLite PWD bit constants that were copy-pasted
across three files into named PublicWeenieFlags.PlayerKiller
(0x20) / PlayerKillerLite (0x02000000) values, routing all three
consumers (AppraisalUiController, CharacterSheetProvider,
LocalPlayerTeleportController) through them. Pure mechanical
substitution; the write side (PlayerKillerStatusBitfield.Apply) is
untouched.
- F16: file register row AD-115 for a real, previously-unrecorded
deviation the AS2 commit introduced: BuildCharacterTitleDisplay
clears element 0x10000151 when neither source resolves, where
retail (CharExamineUI::Show, BasicCreatureExamineUI::Init) never
clears it and would show the previous target's stale title
instead. Marked intentional — ours is deliberately better.
- F17: narrow BuildPlayerKillerDisplay's doc comment — retail's
`cur_weenobj != 0` guard @0x004b482f skips only the PK line, not
the whole response; the whole-response drop on a missing local
object is separate, pre-existing, and out of scope here.
- F18: ledger row for AS2 now reads "review fix round" with the land
commit and a note that the fix SHA is recorded at review-close.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner report 2026-08-25: the examination window on an assessed PLAYER is
missing retail's identity block (gender/heritage, title, PK), the three
per-bodypart armor-level rows, allegiance/faction lines, and the
target-configurable extras. Three-lens research (our pipeline, ACE wire,
named retail decomp) established: the 0x00C9 parse is already complete
(ArmorLevels 0x4000 parsed and dead), everything arrives from ACE, and
the core defect is an element mis-mapping in the Character subview vs
CharExamineUI @0x004AD3C0 (heritage slot fed a raw string prop, title
slot fed AllegianceName, PK slot fed MonarchsName, allegiance slot fed
an invented "Assessment incomplete" literal). Zero Character-subview
tests existed, which is how it survived the Slice 3 gate.
Slices AS2-AS5 (serial, coupled files) + AS6 gate script; ground truth
doc carries the binding line-composition tables, gap ledger G1-G10, and
rulings R1-R8.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Applies all 11 items from the Opus dual-lens review of 989f6652 (0
blockers, 7 SHOULD-FIX, 4 NOTE):
- S2: UiElement.DrawSelfAndChildren now pushes the ambient clip right
after PushAlpha and wraps OnDraw + the children walk +
OnDrawAfterChildren in ONE block — the literal UIRegion::DrawHere
@0x0069FA30 shape, which clips an element's OWN DrawSelf too, not
just its children (UIElement_Text::DrawSelf @0x00467AA0 locks glyph
blits to its own clipped surface rect; UIRegion::DrawSelf
@0x0069F1A0 blits per clip rect). Deleted the two now-redundant
ad-hoc self-clips this supersedes: UiText.DrawText and
UiField.DrawMultiLine both pushed their own (0,0,Width,Height) —
exactly what the new ambient clip already provides one level up.
Kept UiButton.DrawBlockLabel's clip: it clips to LabelBox/ValueBox,
an authored INNER sub-rect that can be smaller than and offset from
the button's own full rect — a genuine narrower viewport, not a
redundant duplicate.
- S3: deleted UiItemList's `ClipsChildren => CellWidth > 0f` override
— correct under the old opt-in-false default, inverted under the
new default-true (an unconfigured list would stop clipping instead
of clipping like everything else).
- S4: pinned the escaped-popup input path end to end. New
UiAncestorClipTests test mounts a menu inside a short window on a
real UiRoot, opens it, and proves a click in the escaped popup
region reaches the menu through UiRoot.PopupHit (a plain top-down
walk is proven to reject the same point first). UiRoot.WantsMouse
now also checks PopupHit — it previously only checked Captured/
HitTestTopDown, so a game action could fire underneath an open
dropdown's escaped region. OnMouseDown/OnScroll already routed
through PopupHit first (#374); unchanged.
- S5: strengthened the Titles-divider regression test's positive
half. The old assertion only checked SOME quad's Y fell in a band —
vacuously true given other same-band content. Now asserts the
divider's exact rect (X and Y), then diffs against the same rect
with the divider hidden (Visible=false) to prove the quad was
actually attributable to it.
- S1: added UiWindowDrawCaptureSweepTests — Character/Chat/Vendor/
Options mounted through their real production Bind entry points
with a non-zero sprite resolver, drawn via RecordingGpuDevice,
asserting a per-window vertex floor (~40-45% of this session's
observed baseline: Character 588, Chat 162, Vendor 54, Options 240)
plus one key sprite id read LIVE off the bound controller/element
(never hardcoded). Character's key sprite (RetailChromeSprites.
TopEdge) specifically exercises OnDrawAfterChildren, the exact path
S2's caution note flagged. Inventory/Paperdoll/social/map-house
skipped — no single fixture-driven top-level Bind entry point.
- S6: added the CT-GF1 subsection to the campaign plan's ledger
(989f6652 + this fix round; CT7 re-gate still owed).
- S7: UiRenderContext.PushClipUnbounded now resets to the CANVAS rect
(0,0,ScreenSize), not null — retail's own popup region is
SCREEN-clipped (UIElement_Menu::MakePopup spawns a top-level region
bounded by the screen), not truly unbounded. AD-113 amended.
- N1: UiRoot overrides ClipsChildren => false — the root's own region
IS the screen (the viewport already scissors it), so this is a
safety net against a momentarily zero-sized root silently blanking
the whole UI tree under the new ancestor-clip default.
- N2: added the empty-clip subtree cull (retail's var_24 gate
@0x0069FB8E) to DrawSelfAndChildren only — DrawOverlays is a wholly
separate traversal untouched by this change. New test proves a menu
inside a fully-clipped (zero-width) window still draws its open
popup via the overlay pass while the main pass draws nothing.
- N3: CT7 script §5 now names the collapsed-toolbar check and the
four highest-overflow windows (combat/vitals bar, Options
bottom-button row, map/house page, floaty chat) as explicit
eyeball items for the re-gate.
- N4: verification below covers both the working tree and the clean
committed tree.
Decomp anchors: UIRegion::DrawHere @0x0069FA30 (var_24 gate
@0x0069FB8E); UIElement_Text::DrawSelf @0x00467AA0 (self-clip);
UIRegion::DrawSelf @0x0069F1A0; UIElement_Menu::MakePopup (screen-
clipped popup region).
Verification (both runs green, --filter "Lane!=InstalledDat&
Lane!=PreparedPackage&Lane!=Live&Lane!=Manual&Lane!=Timing&
Lane!=Windows&Lane!=Linux&Lane!=SystemFont&Purpose!=Diagnostic&
Status!=KnownFailure"): full Release solution build green; working
tree 14,900+ tests across every project (one LandblockPresentation
PipelineTests flake reproduced ONLY under full-solution parallel
load, passes standalone and on rerun — unrelated to this change,
streaming domain); InstalledDat lane green (ACDREAM_RUN_INSTALLED_DAT
_TESTS=1, Status!=KnownFailure, 205+34+3+172 App/Content/Bake/Core
tests). Clean committed tree (git stash push -u the uncommitted
owner probe + docs files, rerun, stash pop) reported in the session
summary.
src/AcDream.App/UI/UiRoot.cs carries an unrelated, pre-existing
uncommitted owner probe (ACDREAM_PROBE_UI_HOVER) — staged selectively
(git add -p) so only this commit's own two hunks (ClipsChildren
override, WantsMouse) landed; the probe hunk is untouched and stays
uncommitted, same as before this fix round.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>