LearnNewsExamplesServices
Frontmatter
id16536
titleA per-move layout flush can be reintroduced and nothing fails
stateClosed
labels
bugaitesting
assigneesneo-opus-grace
createdAtAug 5, 2026, 12:23 PM
updatedAtAug 5, 2026, 3:07 PM
githubUrlhttps://github.com/neomjs/neo/issues/16536
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 16533 A per-move layout heal makes every DOM reorder O(n²): 590-item sort costs 8137ms instead of 18ms
blocking[]
closedAtAug 5, 2026, 3:07 PM

A per-move layout flush can be reintroduced and nothing fails

Closed Backlog/active-chunk-13 bugaitesting
neo-opus-ada
neo-opus-ada commented on Aug 5, 2026, 12:23 PM

Context

Split out of #16533 at review of PR #16534, so two acceptance criteria survive the close rather than retiring silently with it.

The fix is correct and shipping — a live, user-reported animation regression should not wait on a guard that protects against a future reintroduction. But the guard genuinely matters here, because this defect's own history is that reintroduction.

The Problem

The per-move forced layout came from ac231036b5 (#12894, closing #12883) — a grid column-drag repair for Chromium's stale moveBefore flex layout, applied to every moveNode rather than scoped to the case it fixed. Cost scales with the parent's child count, so it was invisible at grid N and quadratic at helix N: 8137ms of blocked main thread on a 590-item reorder against 18ms without it.

Nothing currently fails if someone reintroduces a per-move layout flush. The two ACs below were written to close that, and PR #16534 does not deliver them.

The Fix — and do NOT implement AC1 literally

#16533 AC1 asks for "a timing assertion that fails against the current per-move heal". A wall-clock timing assertion in CI is flaky by construction and will be quarantined within a month, at which point the guarantee is gone and the file still looks covered.

The deterministic form of the same guarantee is structural:

  • a batch of N moveNode deltas against one parent produces exactly one heal, not N
  • moveNode itself never touches parentNode.style.display

Both fail against the per-move form for the right reason, and neither can flake. Assert the shape that made it quadratic, not the wall-clock symptom.

Worth also pinning, since they are the properties a future refactor would silently drop:

  • the drain runs even when a delta throws mid-batch (finally)
  • a parent skipped by the animation gate is not healed, and the batch does not strand it into the next one

Acceptance Criteria

  • Reordering N siblings performs exactly one layout heal per parent, proven by an assertion that fails against the per-move form — RED demonstrated, not asserted.
  • moveNode never performs a layout flush inline; the only flush site is the batch drain.
  • A mid-batch throw still drains pending heals.
  • #12883 stays fixed: a grid column drag to the locked-end region still re-homes correctly.

Out of Scope

  • The fix itself — PR #16534.
  • Visual/animation confirmation, which needs a visible tab; that is #16533's post-merge validation and blocked by the harness reporting document.hidden: true on a fronted tab.
  • The getAnimations({subtree: true}) cost on large pending sets.

Related

#16533 / PR #16534 (the fix) · #12894 / #12883 (heal origin) · #8620

Origin Session ID: c724a85f-2d37-44ac-9a33-12dcce415aa2

tobiu referenced in commit 5942a3b - "test(main): guard the shape that made the layout heal quadratic (#16536) (#16537) on Aug 5, 2026, 3:07 PM
tobiu closed this issue on Aug 5, 2026, 3:07 PM