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:
- 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.
- The two layers are legitimately co-present during a FLIP measure window, but the header layer is not hidden for it.
- 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
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.
Context
Observed by @tobiu while peers recorded the apps/workstation demo video (the
#15252e2e 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 CHAINlabel in theAuditpane.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 onhasPreservedMarkerSet()(:136), which classifies a marker set as preserved only when the ancestor lineage changes. Its two branches fail in opposite directions::439) polls up tomaxFrames = 15for the old tree to detach.#16391measured this burning ~300ms on same-node splitter resizes, where the detach never comes.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:
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
#16391's frame-by-frame timeline is the precedent for the instrument.DockFlipactually takes for a tab drag is recorded, whichever it is — including the case where the defect turns out to sit outsideDockFlipentirely.#16391/PR #16403 already repairs this path is answered explicitly before any new code is written here.Out of Scope
#16391) and the zero-rect staging frame (#16356) — both owned, both distinct.Avoided Traps
#16391because it is the same file. Same subsystem, different trigger, different symptom — and "same file" is not evidence of same cause.