LearnNewsExamplesServices
Frontmatter
id16406
titleTab drag paints the tab header label over the content label for several frames
stateClosed
labels
bugaitesting
assigneesneo-kimi-phoebe
createdAtAug 2, 2026, 10:44 PM
updatedAtAug 9, 2026, 12:06 AM
githubUrlhttps://github.com/neomjs/neo/issues/16406
authorneo-opus-grace
commentsCount5
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 9, 2026, 12:06 AM

Tab drag paints the tab header label over the content label for several frames

Closed Backlog/active-chunk-12 bugaitesting
neo-opus-grace
neo-opus-grace commented on Aug 2, 2026, 10:44 PM

Context

Observed by @tobiu while peers recorded the apps/workstation demo video (the #15252 e2e journey): dragging a tab in the top-right region produces a broken visual frame that then recovers on its own. Reported as feeling like a recent regression, which is the part worth pinning — the surrounding dock work has moved a lot this week.

Live latest-open sweep 2026-08-02T20:2xZ (newest: #16404). Nearest neighbours checked and distinguished below; no equivalent open ticket.

The Problem

The tab header label paints over the content label — @tobiu's reading, from watching it live, and it supersedes my first description of this as "two panel headings". The distinction matters: this is not two peers competing for one slot, it is a label from the header layer landing on top of a label from the content layer. In the captured frame the two strings interleave on one baseline into unreadable glyph soup, under an EVIDENCE CHAIN label in the Audit pane.

It self-recovers, so this is a transient exposure of intermediate state, not a stuck DOM. That is what makes it easy to miss in a manual pass and impossible to miss in a recorded video, which is how it surfaced.

Symptom class: an outgoing subtree and an incoming subtree are both painted for some number of frames, rather than the outgoing one being detached (or hidden) before the incoming one lands.

The Architectural Reality

(Held explicitly as hypothesis — I have not measured this, and a named mechanism in a ticket body silently authorizes a fix. Everything below is a candidate to falsify, not a diagnosis.)

DockFlip.play() (src/main/addon/DockFlip.mjs:357) branches on hasPreservedMarkerSet() (:136), which classifies a marker set as preserved only when the ancestor lineage changes. Its two branches fail in opposite directions:

  • Replacement-tree branch — stage A (:439) polls up to maxFrames = 15 for the old tree to detach. #16391 measured this burning ~300ms on same-node splitter resizes, where the detach never comes.
  • Preserved branch — assumes the nodes survive in place.

A tab drag is structurally different from both cases already studied: the dragged tab genuinely moves, so lineage does change, but the destination pane's content is also swapping. Which branch that lands in, and whether the double-paint happens before or after the classification, is unmeasured.

Candidates, in the order I would test them:

  1. The dragged tab's header label is promoted into a drag layer that is not removed (or not repositioned) before the destination content paints, so a header-layer node overlaps a content-layer node.
  2. The two layers are legitimately co-present during a FLIP measure window, but the header layer is not hidden for it.
  3. A stale header marker from the drag source survives at the destination's coordinates after the drop.

Note the corrected reading narrows this: whatever the branch, the overlap crosses a layer boundary (header vs content), so a candidate that only reorders sibling content nodes cannot explain it.

Relationship to neighbouring tickets

  • #16391 (@neo-kimi-phoebe, PR #16403) — same subsystem, different trigger and different symptom: a committed splitter resize exposed unanimated for ~300ms. There the landed layout is correct but unanimated; here two different contents are painted at once. Her fix may or may not touch this path; that is the first thing to check, and if it does, this becomes a validation case on her PR rather than separate work.
  • #16356 — zero-rect FLIP staging frame on the Security pane. Adjacent exposure class, different pane and trigger.
  • #16357 — second tear-out resumes with the wrong pointer delta. Drag-related, but a pointer-arithmetic defect, not a paint-overlap one.

Acceptance Criteria

  • A witness reproduces the double-paint deterministically: drag a tab in the top-right region and assert that no two heading strings occupy overlapping boxes on any frame.
  • The witness is red before the fix — a transient that only reproduces by eye is not yet a defect anyone can close.
  • The frame count of the exposure is measured and recorded, not described. #16391's frame-by-frame timeline is the precedent for the instrument.
  • The branch DockFlip actually takes for a tab drag is recorded, whichever it is — including the case where the defect turns out to sit outside DockFlip entirely.
  • Whether #16391/PR #16403 already repairs this path is answered explicitly before any new code is written here.
  • The regression window is bounded: identify whether this predates the current dock work or arrived with it.

Out of Scope

  • The splitter-resize exposure (#16391) and the zero-rect staging frame (#16356) — both owned, both distinct.
  • Any broader FLIP redesign. This ticket ends at one reproducible witness plus the smallest repair that turns it green.

Avoided Traps

  • Treating the named mechanism above as the diagnosis. It is a hypothesis from reading, not a measurement. The first commit here should be the witness, not a fix.
  • Assuming it is the same defect as #16391 because it is the same file. Same subsystem, different trigger, different symptom — and "same file" is not evidence of same cause.
  • Closing on "it recovers". Self-recovery is what makes this a visible-artifact bug rather than a broken-state bug; it is not a reason to downgrade it, because the demo video captures exactly these frames.
tobiu referenced in commit 4f2faf7 - "feat(workstation): tab-drag label-overlap witness — no header label paints over a content label (#16406) (#16749) on Aug 9, 2026, 12:06 AM
tobiu closed this issue on Aug 9, 2026, 12:06 AM