fix(vendor): evidence-based pass — max-first stack ceiling; the local player resolves never-animated MoveTo targets
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run

Both chains pinned by the live [vendor-diag] run (vendor-diag.log)
after three code-reading rounds each failed:

The split bar: ACE serializes descStackSize=1 for EVERY browse row
(live wire, log 343-348) — the R1-era "ACE never populates desc"
claim is retracted with the line quoted. Retail's vendor sites read
pwd._maxStackSize directly (four sites, incl. UpdateItemsList
@0x004c1ea0 stamping min(remaining, _maxStackSize));
ResolveAuthoredStackSize flips to max-first for its vendor-only
consumers. Taper ceiling 1000, scarab 100, seed 1 for exempt.
Pricing still reads the desc (per-1 values on ACE).

Walk-to-use: the local player's getObjectA seam was bound to
TryGetPhysicsHost, which resolves only INSTALLED physics hosts — a
never-animated vendor has none, so TargetManager.SetTarget got null,
the MoveToObject armed with zero nodes, and UseTime never dispatched.
The log's natural=False completions were the user's own movement keys
(retail-correct input-edge cancels); attempt 4 worked because the
greeting animation had installed a host. RuntimePhysicsState gains
the retail CObjectMaint::GetObjectA seam (bound canonical resolver
with installed-host fallback); the graphical host binds the SAME
lazy-minimal-host resolver every remote already uses — whose own doc
comment names this exact never-animated hazard. The reservation
release was already correct (2b premise refuted with evidence); the
production-wiring invariants are now pinned by four new tests
including the pre-fix pathology as a permanent sabotage control.

AP-169 rewritten a second time, honestly. The [vendor-diag] probe
family (ACDREAM_DUMP_VENDOR) lands env-gated for future live triage.

Clean-room complete solution: 11,536 passed / 4 skipped / 0 failed.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-08 17:17:04 +02:00
parent d003449bb4
commit 02b735ba4a
21 changed files with 1139 additions and 102 deletions

View file

@ -58,22 +58,21 @@ public readonly record struct VendorShopItem(
// matching retail's own zeroed-struct default of 0 for the same case
// (see VendorPricing.PerUnitValue's <= 0 guard).
int? DescStackSize = null,
// Grand-gate finding R1 (2026-08-08, register AP-169 correction): the
// item TYPE's authored stack ceiling (retail PublicWeenieDesc::
// _maxStackSize, wire AcDream.Core.Net.Messages.PublicWeenieDescBody.
// StackSizeMax). Retail's OWN InqListSlotCount (pc:200038-200065) and
// the Buying tab's Buy Item/Clear Item removal rule
// Grand-gate finding R1, corrected at the 2026-08-08 live-evidence
// re-fix (register AP-169, second correction): the item TYPE's authored
// stack ceiling (retail PublicWeenieDesc::_maxStackSize, wire
// AcDream.Core.Net.Messages.PublicWeenieDescBody.StackSizeMax). This is
// retail's PRIMARY vendor-owned quantity operand, read literally by
// VendorItemsUI::UpdateItemsList (0x004c1ea0, pc:201085-201133 — each
// browse row displays min(remaining, _maxStackSize), plain
// _maxStackSize for unlimited supply), InqListSlotCount (pc:200052),
// and the Buying tab's Buy Item/Clear Item removal rule
// (gmVendorUI::HandleButtonClicks cases 0x100000c9/0x100000cb,
// pc:203989-204010/204080-204094) read THIS field literally, never
// DescStackSize, to decide whether an item is stackable at all. It also
// serves as this port's retail-faithful SUBSTITUTE for the toolbar's
// splitSize/maxSplitSize seed (gmToolbarUI::HandleSelectionChanged,
// pc:198688/198744/198774/198791 — reads PublicWeenieDesc::_stackSize,
// which ACE never populates for a vendor browse-list row; a real retail
// server evidently authors _stackSize == _maxStackSize for an
// unlimited-supply "one full stack" browse listing, so MaxStackSize is
// the value retail's own server would have put there) — see
// VendorSplitPolicy.ResolveAuthoredStackSize and the register (AP-169).
// pc:203996/204086) — never the instance _stackSize. The live ACE wire
// (vendor-diag 2026-08-08) carries descStackSize=1 for every browse
// row, so any desc-first read collapses every vendor stack to 1 — see
// VendorSplitPolicy.ResolveAuthoredStackSize (max-first, desc
// fallback) and the register (AP-169).
int? MaxStackSize = null,
// Review finding F5 (Slice 5.4 review): PublicWeenieDescBody already
// carries these three (IconOverlayId/IconUnderlayId/UiEffects) — see