# Handoff — D.2b inventory: B-Controller shipped + visually confirmed; B-Wire next **Date:** 2026-06-21 (later than the B-Grid handoff of the same date) **From:** the session that shipped **B-Controller** (inventory population + burden meter + captions) and root-caused/fixed the **backdrop wash-out** (a #145 continuation) at the visual gate. **Predecessor:** `docs/research/2026-06-21-d2b-bgrid-shipped-bcontroller-next-handoff.md` (window manager + B-Grid). This doc supersedes it for "what's next." **Branch:** `claude/hopeful-maxwell-214a12`, tip **`c38f098`**. `main` is a clean fast-forward ancestor — ff `main` + branch fresh, or continue this branch. **Line numbers drift — grep the symbol.** --- ## 0. What shipped this session (all committed, build + tests green, VISUALLY CONFIRMED) **B-Controller** — `InventoryController` (the `gm*UI::PostInit` analogue) binds the imported `gmInventoryUI 0x21000023` tree by id and populates it from `ClientObjectTable`. F12 (with `ACDREAM_RETAIL_UI=1`) now shows your live inventory on the dark backdrop: the "Contents of Backpack" grid, the pack-selector strip, the vertical burden bar ("Burden 17%"), and the captions. Commits (after the input handoff's `4e23a7b`): - `5875ac8` `BurdenMath` encumbrance ports (Core) - `fb050ae` `ClientObjectTable.SumCarriedBurden` (Core) - `5e75d2a` `UiMeter` vertical fill (`Vertical`/`FillFromBottom`/`DrawVBar`) - `89c640a` `InventoryController` bind + populate + burden + captions - `383e8b7` follow-up (Populate uses `GetContents` only — dropped a subagent's index-fallback workaround) - `03fbf44` GameWindow wiring (the `InventoryController.Bind` in the inventory-init block) - `7bb4bd3` divergence rows AP-48..51 + ISSUES/roadmap - `1ccf07b` Opus phase-boundary review fixes (burden % saturation + equipped-item filter) - `417b137` **render fixes** (backdrop wash-out [#145 continuation] + captions) — the visual-gate fix - `c38f098` docs (ISSUES/roadmap: visually confirmed + #145 continuation + overflow follow-up) Spec: `docs/superpowers/specs/2026-06-21-d2b-inventory-controller-design.md`. Plan: `docs/superpowers/plans/2026-06-21-d2b-inventory-controller.md`. Tests: **App 532 / Core 1526 green.** `InventoryFrameImportProbe` is a real-dat smoke test (gated on `ACDREAM_DAT_DIR`) that **locks the ZLevel fix** (asserts each mounted panel's ZOrder > the backdrop's). --- ## 1. Read first - This doc. - `claude-memory/project_d2b_retail_ui.md` — the B-Controller SHIPPED entry + DO-NOT-RETRY (updated this session). - `docs/superpowers/specs/2026-06-21-d2b-inventory-controller-design.md` — the B-Controller spec. - `docs/research/2026-06-16-inventory-deep-dive.md` §4 — the inventory wire catalog (for **B-Wire**). - `docs/research/2026-06-16-ui-panels-synthesis.md` §3.3 — the de-duped wire-gap TODO. - `docs/ISSUES.md` — the **#145** entry (now closed via the continuation), the **D.2b-B SHIPPED** entry, and the new **contents-grid overflow** issue. --- ## 2. Key discoveries (durable, non-obvious — DO-NOT-RETRY) 1. **THE BIG ONE — a mounted sub-window slot must keep its OWN frame ZLevel, NOT inherit the base layout root's.** The gm*UI sub-window roots (`0x100001C8` backpack, `0x100001C4` 3D-items) carry **ZLevel 1000**. `ElementReader.Merge`'s zero-wins-base rule (`ElementReader.cs:161`, `ZLevel = derived.ZLevel != 0 ? derived.ZLevel : base_.ZLevel`) made the mounted slots (own ZLevel 0) inherit that 1000, and the #145 ZOrder fold (`ReadOrder − ZLevel·10000`) turned 1000 into ZOrder ≈ −10,000,000 — *behind* the frame's Alphablend backdrop (`0x100001D0`, ZLevel 100 → ≈ −1,000,000). The backdrop then **overpainted/washed out** the panels' captions + burden meter + item cells. The **paperdoll** root happens to be ZLevel 0, so it survived — which is why the B-Grid session believed #145 was "done." Fix (`417b137`): `LayoutImporter.Resolve` sets `result.ZLevel = self.ZLevel` inside the `ShouldMountBaseChildren` block. **Root-caused via a one-shot `TextRenderer` sprite-segment-order dump** (the backdrop's segment was drawing AFTER the panel content) + a live ZLevel probe — when "drawn but invisible," dump the actual paint order, don't keep theorizing z-math. 2. **Burden formula (ported into `Core.Items.BurdenMath`):** `capacity = Str × (150 + clamp(aug×30, 0, 150))` (`EncumbranceSystem::EncumbranceCapacity` `0x004fcc00`); `load = burden / capacity` (`EncumbranceSystem::Load` `0x004fcc40` — the decompiler MANGLED the FP divide to `return burden`; cross-checked vs the existing `ComputeMax` + the r06 doc); `gmBackpackUI::SetLoadLevel` (`0x004a6ea0`) → fill = `clamp(load/3, 0, 1)`, %text = `floor(load × 100)` **SATURATING at 300%** (computed from the CLAMPED fill, not unclamped — caught in review). `CACQualities::InqLoad` `0x0058f130`. **acdream has no wire `EncumbranceVal` yet** → client-side `SumCarriedBurden` fallback (divergence **AP-48**; B-Wire pins the real wire value). 3. **`DatWidgetFactory.BuildMeter`'s single-image path is ALREADY correct for the burden meter** (meter-own DirectState `0x0600121D` = back/track; its one Type-3 child `0x0600121C` = fill) per `UIElement_Meter::DrawChildren` `0x0046fbd0` (the child = `m_pcChildImage = GetChildRecursive(this, 2)`). The inventory deep-dive's "0x0600121C = back" label was backwards re: draw role. The only meter change was a **vertical** fill path (`UiMeter.Vertical`/`FillFromBottom`/`DrawVBar`). A near-empty fill at low load is CORRECT (Str 290 → 17% → ~3px of a 58px bar), not a bug. 4. **Captions: drive the HOST `UiText` directly.** The caption elements (`0x100001D7` "Burden", `0x100001D8` %, `0x100001C5` "Contents of Backpack") resolve to `UiText`. A nested *child* `UiText` does NOT paint — `AttachCaption` now sets the host UiText's `LinesProvider`/`Centered`/`DatFont`. 5. **Equipped items excluded** from the pack grid + selector (`CurrentlyEquippedLocation != None`) — a mid-session self-wield routes them through `MoveItem(item, WielderGuid=player)` into `GetContents(player)`; retail's gm3DItemsUI shows pack contents only. --- ## 3. Current visual state (F12, `ACDREAM_RETAIL_UI=1`) Renders correctly: dark backdrop BEHIND; paperdoll equip slots (generic blue border); backpack strip ("Burden" + % + vertical bar + side-bag cell + main-pack cell); 3D-items "Contents of Backpack" grid populated (6-col). **Expected gaps (all are the follow-ups below):** - Contents grid OVERFLOWS the 96px panel (no scroll) — "part hanging off the window." - Paperdoll equip slots show a generic blue `UiDatElement` border, not per-slot silhouettes → Sub-phase C. - The inventory window starts HIDDEN; F12 toggles it (the auto-open debug hack was reverted). --- ## 4. What's next (build order; each gets its own brainstorm → spec → plan → subagent flow) **(a) B-Wire — RECOMMENDED NEXT.** Parse the wire `EncumbranceVal` (ACE `PropertyInt 5`) for the player so the burden bar reads the server value (retires **AP-48**, the client-side sum). Plus the other inventory wire gaps catalogued in `2026-06-16-inventory-deep-dive.md §4` + the de-duped TODO in `2026-06-16-ui-panels-synthesis.md §3.3`: builders `DropItem 0x001B`, `GetAndWieldItem 0x001A`, `NoLongerViewingContents 0x0195`; parsers `ViewContents 0x0196`, `SetStackSize`, `InventoryRemoveObject`; fix the dropped 4th field on `0x0022` + the dropped error on `0x00A0`; register the unwired parsers; extend `CreateObject` if any icon/stack fields are still discarded. **Mandatory grep-named → cross-ref → pseudocode → port** for each wire format; ACE handlers + holtburger fixtures are the oracles. **(b) Contents-grid scroll polish.** The 6-col grid shows ALL loose items, overflowing the 96px panel. Wire the gutter `UiScrollbar` (`0x100001C7`) to the grid + clip the grid to the panel + scroll the overflow. Filed as an OPEN issue in `docs/ISSUES.md`; was out-of-scope for B-Controller (spec §10). Small, self-contained. **(c) B-Drag.** Inventory cell as a drag SOURCE (reuse the shipped spine — `UiItemSlot` is already a full drag source/target) + the `SourceKind == Inventory` branch in `ToolbarController.HandleDropRelease`. **(d) Sub-phase C — the biggest piece.** Paperdoll: register `0x10000032` (`UiItemSlot`) so equip slots draw per-slot silhouettes (fixes the generic-blue-slot art) + the `UiViewport` (Type `0xD`) 3D character doll, which needs a new Core→App `IUiViewportRenderer` seam (Code-Structure Rule 2). Research: `docs/research/2026-06-16-equipment-paperdoll-deep-dive.md`. (Then D.5.3-remaining: the toolbar mana meter + stack slider + spell shortcuts; vendor/trade/spellbook.) --- ## 5. Caveats / open items - **Chat + toolbar #145 z-order eyeball (the input handoff's first ask):** analytically cleared this session — chat's only non-zero-ZLevel element (`0x1000000E`, ZLevel 900) was already the backmost top-level sibling, so the #145 fold can't change its order; the toolbar now honors ZLevel (retail-faithful). No regression seen across ~10 launches. Not separately user-confirmed in isolation, but low-risk — glance once if convenient. - The `InventoryFrameImportProbe` real-dat test SKIPS in CI (no dat). It only asserts when `ACDREAM_DAT_DIR` (or the default `~/Documents/Asheron's Call`) exists. - `ClientObjectTable.GetContents(playerGuid)` returns `IReadOnlyList` (guids), NOT `ClientObject`s — a prior research agent's claim was wrong; trust the source. - The main-pack cell (`m_topContainer 0x100001C9`) uses a placeholder icon (the main pack ≡ the player, no item IconId) — AP-51. Pin a backpack RenderSurface DID if it bothers you. --- ## 6. New-session prompt (paste into a fresh session) > Continue acdream's D.2b retail-UI inventory arc. **Read > `docs/research/2026-06-21-d2b-bcontroller-shipped-next-handoff.md` first.** B-Controller > (inventory population + burden meter + captions) shipped + visually confirmed on > `claude/hopeful-maxwell-214a12` (tip `c38f098`; `main` is a clean ff ancestor — ff + branch > fresh, or continue). The backdrop wash-out (a #145 continuation: mounted sub-window slots were > inheriting the base root's ZLevel 1000 → behind the backdrop) is fixed. **Next: B-Wire** — parse > the player's wire `EncumbranceVal` (PropertyInt 5) to retire the client-side burden-sum fallback > (AP-48), plus the inventory wire gaps in `2026-06-16-inventory-deep-dive.md §4` / > `2026-06-16-ui-panels-synthesis.md §3.3`. Use the full brainstorm → spec → plan → subagent-driven > flow; mandatory grep-named → cross-ref → pseudocode → port for every wire format; conformance > tests throughout. After B-Wire: the contents-grid scroll polish (wire gutter `0x100001C7`), > B-Drag (inventory drag SOURCE), then Sub-phase C (paperdoll `UiViewport` doll + `0x10000032` > UiItemSlot per-slot art). **MEMORY.md index line** (already added this session): - [Project: D.2b retail UI engine](project_d2b_retail_ui.md) — … Inventory B-Controller SHIPPED 2026-06-21. DO-NOT-RETRY: a mounted sub-window slot must keep its OWN frame ZLevel — inheriting the base root's ZLevel 1000 sinks the panel behind the Alphablend backdrop (the #145-continuation wash-out).