Context
#16533 fixed the DeltaUpdates half of the helix sort regression (per-move layout heal → batched + skipped mid-transition, PR #16534, merged). It closed with three Helix-side findings explicitly deferred. This ticket carries them so they do not retire with it.
Operator, after testing the merged fix on a visible browser: "first sort button click has an animation, but still VERY slow compared to e.g. a year ago". The heal was the animation killer; the items below are the remaining cost and the remaining fragility.
1. Every sort reorders the DOM twice
Helix.sortItems (src/component/Helix.mjs:1059) hand-writes one moveNode delta per item straight to Neo.applyDeltas, bypassing the VDom worker. The differ never sees those moves, so it emits its own reorder afterwards. Measured on 590 items — three batches per sort:
| t |
batch |
contents |
| ~20ms |
1 |
{moveNode: 590} — sortItems' manual deltas |
| ~50ms |
2 |
{moveNode: 586, updateNode: 590} — the differ's own reorder |
| ~82ms |
3 |
{updateNode: 590} |
~1176 moves where 590 would do. Node reordering is the expensive operation here — transform updates are not (updateNode cost 7ms across 1182 calls in the same run, which is why mouse-wheel rotation sustains ~70k deltas/sec untouched).
Falsified while investigating, so nobody re-chases it: this is not vdom/DOM order divergence. Probing each item's matrix rotation component by DOM index gave a constant 13 direction changes before and after both sorts — that count is the spiral wrapping, not disorder. The order is correct; the work is duplicated.
Worth asking whether the manual moveNode pass is needed at all now, given the differ reorders anyway.
2. The transition window is anchored to the wrong event
applyItemTransitions (:462) adds neo-transition-1000, invokes the callback, then schedules class removal with setTimeout(animationTime + 200) — a wall-clock timer started when the class is added. But the transition can only begin when the transform is applied, after an unbounded amount of work.
Before #16533 this lost outright: the transform landed at 6780ms and the class was removed at 6793ms, 13ms later. With the heal batched it currently wins with ~1134ms of margin — but nothing enforces that, and any slow frame reintroduces the failure. The window should be anchored to the transform application or to transitionend, not to the class add.
3. neo-transition-600 matches no CSS rule
Added at Helix.mjs:747 and :923-929; resources/scss/src/component/Helix.scss defines only neo-transition-100 and neo-transition-1000. Either the rule is missing or the class is dead — both are cheap to resolve and one of them is a silent no-op today.
Acceptance Criteria
Verification bound, stated because it cost a wrong claim on #16533
The animation result cannot be verified from the agent harness. The Claude browser pane reports document.hidden: true even when fronted, and background tabs neither composite nor run transitions nor fire transition events, while throttling timers to ~1/sec. JS-timing evidence proves only a necessary condition. npm run test-components renders properly and is the right home for anything animation-dependent; final visual confirmation is an operator/visible-tab gate.
Out of Scope
DeltaUpdates — fixed in #16533 / PR #16534; its regression guard is #16536.
- Sorting for buffered stores, which is why the sort buttons are disabled below 600 items.
Decision Record impact
none — component-internal.
Related
#16533 / PR #16534 (the merged half) · #16536 (its guard) · #12894 / #12883 (heal origin).
Context
#16533fixed theDeltaUpdateshalf of the helix sort regression (per-move layout heal → batched + skipped mid-transition, PR #16534, merged). It closed with three Helix-side findings explicitly deferred. This ticket carries them so they do not retire with it.Operator, after testing the merged fix on a visible browser: "first sort button click has an animation, but still VERY slow compared to e.g. a year ago". The heal was the animation killer; the items below are the remaining cost and the remaining fragility.
1. Every sort reorders the DOM twice
Helix.sortItems(src/component/Helix.mjs:1059) hand-writes onemoveNodedelta per item straight toNeo.applyDeltas, bypassing the VDom worker. The differ never sees those moves, so it emits its own reorder afterwards. Measured on 590 items — three batches per sort:{moveNode: 590}—sortItems' manual deltas{moveNode: 586, updateNode: 590}— the differ's own reorder{updateNode: 590}~1176 moves where 590 would do. Node reordering is the expensive operation here — transform updates are not (
updateNodecost 7ms across 1182 calls in the same run, which is why mouse-wheel rotation sustains ~70k deltas/sec untouched).Falsified while investigating, so nobody re-chases it: this is not vdom/DOM order divergence. Probing each item's matrix rotation component by DOM index gave a constant 13 direction changes before and after both sorts — that count is the spiral wrapping, not disorder. The order is correct; the work is duplicated.
Worth asking whether the manual
moveNodepass is needed at all now, given the differ reorders anyway.2. The transition window is anchored to the wrong event
applyItemTransitions(:462) addsneo-transition-1000, invokes the callback, then schedules class removal withsetTimeout(animationTime + 200)— a wall-clock timer started when the class is added. But the transition can only begin when the transform is applied, after an unbounded amount of work.Before
#16533this lost outright: the transform landed at 6780ms and the class was removed at 6793ms, 13ms later. With the heal batched it currently wins with ~1134ms of margin — but nothing enforces that, and any slow frame reintroduces the failure. The window should be anchored to the transform application or totransitionend, not to the class add.3.
neo-transition-600matches no CSS ruleAdded at
Helix.mjs:747and:923-929;resources/scss/src/component/Helix.scssdefines onlyneo-transition-100andneo-transition-1000. Either the rule is missing or the class is dead — both are cheap to resolve and one of them is a silent no-op today.Acceptance Criteria
neo-transition-600either matches a rule or is removed; no class is added that nothing styles.Verification bound, stated because it cost a wrong claim on
#16533The animation result cannot be verified from the agent harness. The Claude browser pane reports
document.hidden: trueeven when fronted, and background tabs neither composite nor run transitions nor fire transition events, while throttling timers to ~1/sec. JS-timing evidence proves only a necessary condition.npm run test-componentsrenders properly and is the right home for anything animation-dependent; final visual confirmation is an operator/visible-tab gate.Out of Scope
DeltaUpdates— fixed in#16533/ PR #16534; its regression guard is#16536.Decision Record impact
none— component-internal.Related
#16533/ PR #16534 (the merged half) ·#16536(its guard) ·#12894/#12883(heal origin).