PR #17557 implements the #17554 fixed-height buffered component-list foundation: bounded physical slots, full-Store semantics, and zero steady-state row insertion/removal. Grid has a stronger, older pooling contract. Neo.grid.Body keeps row children in fixed physical order and repositions/rebinds them in place; Neo.grid.Row renders a dense cell pool, including hidden placeholders, so scrolling emits no structural DOM operations.
The historical progression is captured by #8992, #9002, and #9012: bounded allocation alone was insufficient while child rotation could still produce moveNode; the Grid contract became fixed physical order plus logical metadata.
Live latest-open sweep: checked the newest 20 open issues at 2026-08-22T17:23:55Z; no equivalent ticket found. The latest 30 A2A messages contained only the existing #17554 foundation claim, not this successor.
The Problem
Neo.list.Buffered#assignPoolSlots() currently preserves a surviving record's physical slot, then returns descriptors in logical Store order. createItems() writes those descriptors into vdom.cn in that logical order. When the mounted range advances, stable slot IDs can therefore rotate within the child array.
The current regression witness proves zero insertNode and zero removeNode, but it does not capture or assert moveNode. This is a source-proven contract gap, not a claimed runtime count: the first deliverable is an exact delta baseline.
The list cannot copy Grid's modulo/transform strategy blindly. A semantic <ul>/<li> must retain Store-order DOM and accessibility traversal; visual-only reordering would make the painted order and assistive reading order disagree.
src/grid/Row.mjs:286-305,409-421 owns the dense fixed-order cell pool.
src/list/Buffered.mjs:247-288 owns logical-record to physical-slot assignment.
src/list/Buffered.mjs:368-389 materializes top spacer, pooled rows, and bottom spacer.
test/playwright/unit/list/Buffered.spec.mjs:157-183 captures VDOM deltas but leaves moveNode unasserted.
List selection, focus, data.recordId, aria-posinset, and aria-setsize are logical-record contracts; physical slot IDs are implementation identity.
The list-specific fixed-order shape is slot-to-viewport-position affinity: keep slot-0 … slot-N in invariant DOM order and bind them to mountedStart + slotIndex. Component instances remain slot-owned and bounded. Surviving records may change physical component slot when the window advances; logical focus and selection must be restored through record metadata.
The Fix
Extend the existing range-movement witness to record all structural VDOM actions and establish the current exact baseline.
Make the steady-state child order invariant: [top spacer, slot-0 … slot-N, bottom spacer].
Bind each physical slot to the corresponding logical offset in the mounted range instead of rotating slot IDs to preserve record affinity.
Keep one component instance per physical slot; scrolling reconfigures those instances without creating or destroying components.
Remap selection, focus, record updates, and scroll anchoring through logical record identity after slot rebinding.
Document the chosen slot-owned identity contract in Neo.list.Buffered JSDoc and protect it with unit witnesses.
Contract Ledger
Target surface
Source of authority
Proposed behavior
Fallback
Docs
Evidence
assignPoolSlots()
src/list/Buffered.mjs
Physical slot order is invariant; logical records bind by mounted-range offset
None; fail the fixed-order witness
Method JSDoc
Forward/reverse range tests
Buffered root vdom.cn
createItems()
Same spacer and slot IDs remain at the same child indexes while pool size is unchanged
Resize may intentionally grow/shrink the pool
Class + method JSDoc
Zero insert/remove/move deltas
Pooled component lifetime
getPooledComponent() / trimComponentPool()
One stable component instance per physical slot during scrolling
Excess slots may be destroyed only on pool shrink
Method JSDoc
Identity/allocation witness
Logical interaction state
record metadata + selection/focus methods
Selection, focus, anchoring, and ARIA follow the logical record after rebinding
Fail visible; no stale slot fallback
Existing contracts updated as needed
Interaction + semantic-order tests
Decision Record impact
None. This aligns Neo.list.Buffered with the established Grid pooling principle while retaining list-specific DOM semantics.
Acceptance Criteria
A delta witness proves that the mounted range actually advances and records the exact pre-change counts for moveNode, insertNode, and removeNode.
With an unchanged pool size, forward and reverse range movement produce zero moveNode, zero insertNode, and zero removeNode.
Physical row IDs and their child indexes remain invariant across those movements.
The component instance set and cardinality remain invariant during scrolling; no pooled component is created or destroyed.
Rendered row data.recordId order matches the mounted Store slice after every movement.
aria-posinset, aria-setsize, selection, scroll anchoring, record updates, and focus target the correct logical records across slot rebinding.
Pool growth/shrink remains explicitly resize-owned and is not misclassified as a steady-state structural regression.
Targeted Neo.list.Buffered tests and the repository unit suite pass.
Context
PR #17557 implements the
#17554fixed-height buffered component-list foundation: bounded physical slots, full-Store semantics, and zero steady-state row insertion/removal. Grid has a stronger, older pooling contract.Neo.grid.Bodykeeps row children in fixed physical order and repositions/rebinds them in place;Neo.grid.Rowrenders a dense cell pool, including hidden placeholders, so scrolling emits no structural DOM operations.The historical progression is captured by
#8992,#9002, and#9012: bounded allocation alone was insufficient while child rotation could still producemoveNode; the Grid contract became fixed physical order plus logical metadata.Live latest-open sweep: checked the newest 20 open issues at 2026-08-22T17:23:55Z; no equivalent ticket found. The latest 30 A2A messages contained only the existing
#17554foundation claim, not this successor.The Problem
Neo.list.Buffered#assignPoolSlots()currently preserves a surviving record's physical slot, then returns descriptors in logical Store order.createItems()writes those descriptors intovdom.cnin that logical order. When the mounted range advances, stable slot IDs can therefore rotate within the child array.The current regression witness proves zero
insertNodeand zeroremoveNode, but it does not capture or assertmoveNode. This is a source-proven contract gap, not a claimed runtime count: the first deliverable is an exact delta baseline.The list cannot copy Grid's modulo/transform strategy blindly. A semantic
<ul>/<li>must retain Store-order DOM and accessibility traversal; visual-only reordering would make the painted order and assistive reading order disagree.The Architectural Reality
src/grid/Body.mjs:30-37,710-713,787-800owns fixed-DOM-order row recycling.src/grid/Row.mjs:286-305,409-421owns the dense fixed-order cell pool.src/list/Buffered.mjs:247-288owns logical-record to physical-slot assignment.src/list/Buffered.mjs:368-389materializes top spacer, pooled rows, and bottom spacer.test/playwright/unit/list/Buffered.spec.mjs:157-183captures VDOM deltas but leavesmoveNodeunasserted.data.recordId,aria-posinset, andaria-setsizeare logical-record contracts; physical slot IDs are implementation identity.The list-specific fixed-order shape is slot-to-viewport-position affinity: keep
slot-0 … slot-Nin invariant DOM order and bind them tomountedStart + slotIndex. Component instances remain slot-owned and bounded. Surviving records may change physical component slot when the window advances; logical focus and selection must be restored through record metadata.The Fix
[top spacer, slot-0 … slot-N, bottom spacer].Neo.list.BufferedJSDoc and protect it with unit witnesses.Contract Ledger
assignPoolSlots()src/list/Buffered.mjsvdom.cncreateItems()getPooledComponent()/trimComponentPool()Decision Record impact
None. This aligns
Neo.list.Bufferedwith the established Grid pooling principle while retaining list-specific DOM semantics.Acceptance Criteria
moveNode,insertNode, andremoveNode.moveNode, zeroinsertNode, and zeroremoveNode.data.recordIdorder matches the mounted Store slice after every movement.aria-posinset,aria-setsize, selection, scroll anchoring, record updates, and focus target the correct logical records across slot rebinding.Neo.list.Bufferedtests and the repository unit suite pass.Out of Scope
list.plugin.Animatebehavior.Avoided Traps
Related
Blocked by #17554.
Related: PR #17557, #8992, #9002, #9012.
Origin Session ID: f47f948b-743b-4c11-84a8-fa60a567a148
Retrieval Hint:
list.Buffered fixed DOM order slot-to-viewport affinity moveNode Grid pooling