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
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:
parent
d003449bb4
commit
02b735ba4a
21 changed files with 1139 additions and 102 deletions
|
|
@ -136,6 +136,11 @@ public sealed class VendorShopItemMaterializerTests
|
|||
/// <c>DescStackSize</c>, packed <c>StackSize=-1</c> (unlimited),
|
||||
/// <c>MaxStackSize=1000</c> -> <c>ClientObject.StackSize</c> resolves
|
||||
/// to 1000, not 1 and not the (nonsensical, unbounded) packed field.
|
||||
/// (Second correction, 2026-08-08: the live wire showed ACE actually
|
||||
/// sends <c>descStackSize=1</c>, so <c>MaxStackSize</c> is now the
|
||||
/// PRIMARY operand rather than a fallback — see
|
||||
/// <see cref="Apply_LiveAceWireShape_DescOneMaxHundred_ResolvesToTheAuthoredCeiling"/>;
|
||||
/// this desc-absent case resolves identically either way.)
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Apply_UnlimitedStockNoDescStackSize_FallsBackToMaxStackSize()
|
||||
|
|
@ -157,6 +162,42 @@ public sealed class VendorShopItemMaterializerTests
|
|||
Assert.Equal(1000, item.StackSizeMax);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 2026-08-08 live-evidence re-fix (register AP-169, second
|
||||
/// correction): the EXACT wire shape the vendor-diag run captured from
|
||||
/// the live ACE server — `descStackSize=1 stackSizeMax=100` for every
|
||||
/// browse row (e.g. the Smelting Pot / Lead Scarab rows,
|
||||
/// `[vendor-diag] ApproachVendor wire-item[0] ... descStackSize=1
|
||||
/// stackSizeMax=100`). ACE DOES serialize the instance stack size, at
|
||||
/// the useless value 1, so the R1 desc-first preference resolved every
|
||||
/// vendor stack to 1 and the toolbar split slider never appeared
|
||||
/// (`ApplySelection ... failingPredicate=stackSize<=1u`). Retail's
|
||||
/// own vendor UI reads <c>pwd._maxStackSize</c> directly
|
||||
/// (<c>VendorItemsUI::UpdateItemsList</c> <c>0x004c1ea0</c>,
|
||||
/// <c>pc:201085-201133</c>), so the materialized ceiling must be 100
|
||||
/// here. Sabotage-verified: restoring the desc-first preference makes
|
||||
/// this resolve 1 and fail.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Apply_LiveAceWireShape_DescOneMaxHundred_ResolvesToTheAuthoredCeiling()
|
||||
{
|
||||
var vendor = new VendorState();
|
||||
var objects = new ClientObjectTable();
|
||||
using var materializer = new VendorShopItemMaterializer(vendor, objects);
|
||||
|
||||
vendor.Apply(VendorGuid, default, new[]
|
||||
{
|
||||
new VendorShopItem(
|
||||
ItemA, StackSize: -1, WeenieClassId: 1u, Name: "Lead Scarab",
|
||||
ItemType: (uint)ItemType.SpellComponents, IconId: 0x1234u, Value: 10,
|
||||
DescStackSize: 1, MaxStackSize: 100),
|
||||
});
|
||||
|
||||
ClientObject item = objects.Get(ItemA)!;
|
||||
Assert.Equal(100, item.StackSize);
|
||||
Assert.Equal(100, item.StackSizeMax);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sabotage-adjacent control: the SAME unlimited-stock listing but with
|
||||
/// <see cref="VendorShopItem.MaxStackSize"/> ALSO absent (neither wire
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue