ItemHandsColumn ran a fresh .Select(...).ToArray() over _itemRows, PLUS a
per-row BaseItemName suffix-strip to recover the undecorated name, on
every single retained-UI draw. Folded per item F12: RefreshItemEditors
now captures the raw SortedCombatItemNames() result once as
_itemBaseNames — the SAME array used both to build the decorated
_itemRows (append " [no buffs]" where it applies) and to compute
_itemHandsColumn directly, so the " [no buffs]" suffix has exactly one
definition (added going forward) instead of two (added in RefreshItemEditors,
parsed back off in the old ItemHandsColumn getter).
CycleItemHandsAtCore (the grid's own "click cycles handedness" cell
action) mutates _itemHandedness but did not call RefreshItemEditors —
unlike the Monsters grid's mutators, which all already refresh after
mutating. Added the call so the cached column stays correct; also
switched it to read the cached _itemBaseNames instead of a second
SortedCombatItemNames() call.
Mutation named: temporarily restored the old live-recomputing
ItemHandsColumn getter (with a local copy of the suffix-stripping helper)
and confirmed the new
ItemHandsColumnDoesNotReallocateOnEveryReadAndNoBuffSuffixNeverLeaks pin
fails (Assert.Same throws — different array instances per read) before
restoring the fix. The same test also proves the no-buffs case still
resolves handedness correctly through the folded base-name array.
MossTank suite 680 -> 681 (one new pin). Full solution build green; App
markup/plugin filter 203/203.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>