LearnNewsExamplesServices
Frontmatter
id18731
titlePopup-over-popup drop zones never refresh after a resize: both overlap arms red
stateClosed
labels
bugaitestingdashboard
assigneesneo-opus-ada
createdAtSep 15, 2026, 11:50 AM
updatedAtSep 15, 2026, 9:27 PM
githubUrlhttps://github.com/neomjs/neo/issues/18731
authorneo-opus-grace
commentsCount8
parentIssue18303
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 15, 2026, 9:27 PM

Popup-over-popup drop zones never refresh after a resize: both overlap arms red

Closed v13.2.0/archive-v13-2-0-chunk-21 bugaitestingdashboard
neo-opus-grace
neo-opus-grace commented on Sep 15, 2026, 11:50 AM

Context

#18303's last open acceptance row is "the full actual-pointer overlap journey". That journey is test/playwright/e2e/workstation/WorkstationNativePopupOverPopupNL.spec.mjs, and two of its three arms are red with no owner.

Measured in the #17844 battery census at dev@03219a75b7 (2026-09-15, headless, --workers=1, NEO_AGENTOS_RUNTIME_ROOT wired):

arm first error
an enlarged popup updates its drop zones before another popup transfers into the newly exposed area the target is substantially wider
resizing an already-hovered popup refreshes its drop zones without leaving the target the target is substantially wider

The third arm, the OS-titlebar move, passes.

They lost their owner when #18121 closed on 2026-09-13 with both still red. That closure was correct — PR #18681 repaired what #18121 actually owned — but its re-scoped AC-1 says explicitly where anything left over goes:

The three 2026-09-02 sightings are preserved as unattributed, not discharged and not silently dropped: they belong to whoever next reproduces one, under their own ticket, and this lane does not owe their causes.

This is that ticket. #17844 records the reds but cannot own them — its own body states "this ticket owns evidence, not repair" and "any red is either fixed forward (linked leaf) or explicitly dispositioned here." A census row is a record, not an owner.

Live latest-open sweep: latest 20 open issues checked 2026-09-15T09:52Z; no equivalent. Full-text sweep over open and closed for NativePopupOverPopup / popup-over-popup / drop zones: #18683 (closed, Participation subclass), #16472 / #16358 (open, large-over-small conversion and park — a different failure), #18555 (closed, DevTools offset). None owns a resize-driven zone refresh. A2A claim sweep over the last 30 messages, all read-states: no competing claim.

The Problem

This is a fourth symptom, not a recurrence of #18121's three. Those were placing the target vessel did not hand it to the main window, a target popup closed during the placement wait, and a same-tick claim tie resolving to workstation-main. The present failure is none of them: the target vessel is placed and claimed, and the arm fails later, on the geometry of the target after a resize.

Both red arms share one shape — a popup is resized, and the drop zones the drag hit-tests are read from a rect that no longer matches the window:

  • the enlarged-popup arm grows the target and expects the newly exposed area to accept a transfer;
  • the already-hovered arm resizes under an active hover and expects the zones to refresh without the pointer leaving the target.

the target is substantially wider is the arm's own guard reporting that the measured target does not match what it enlarged it to — i.e. the reader saw a pre-resize width.

#18121 already named the mechanism that makes this plausible, for a different symptom: src/manager/Window.mjs refreshes outerRect on connect and on position change, so a resize without a move leaves outerRect stale (#18068's note). Both of these arms resize without moving. That is a hypothesis with a named mechanism and a recorded precedent — not an attribution; see AC-1.

The Architectural Reality

  • test/playwright/e2e/workstation/WorkstationNativePopupOverPopupNL.spec.mjs — the three arms; Brain-gated, so no CI workflow selects it (#18246).
  • src/manager/Window.mjs — outerRect refresh triggers; the resize-without-move gap.
  • src/manager/DragCoordinator.mjs#getNativeWindowDropCandidate → getWindowAtExcept, which hit-tests manager.Window rects.
  • src/dashboard/dock/** — the drop-zone projection consuming those rects.
  • #18400 (closed) delivered "refresh popup drop geometry when its active target is resized" under #18303. Whether that repair covers this path or stops short of it is the first thing to measure — if it covers it, this ticket's answer may be that the arm asserts a stale contract rather than that the engine is wrong.

The Fix

Attribution before repair, because the same spec has already produced three sightings whose causes were never established:

  1. Run the whole file (never a --grep'd arm — #17844 trap 3) with the Brain runtime wired and capture, at the failing frame: the target's real window geometry, manager.Window.toJSON() for main and both vessels, and the drop-zone rects the coordinator hit-tests.
  2. Decide between the two candidates the capture separates: stale outerRect after a resize-without-move (engine) versus the arm asserting a width contract #18400 deliberately changed (spec). They are distinguishable — one shows a rect that disagrees with the live window, the other shows agreement and a wrong expectation.
  3. Fix whichever it is, red-first: the arm must fail before the change and pass after, on the same head.
  4. If the cause is engine-side, state whether #18400's repair was incomplete or the path differs, so the two are not silently merged.

Acceptance Criteria

  • AC-1 — The failure is attributed from a captured failing run, not inferred. Attributed 2026-09-15 by @neo-opus-ada, three whole-file runs at --workers=1 with the Brain runtime named: the arms fail at test/playwright/fixtures.mjs:138 on worker-console errors and never reach a geometry assertion, from two independent causes. Cause 1 — 21 duplicate Observable registrations from tab.plugin.Overflow's unbounded mounted subscription — is discharged by #18748 / #18749, measured 21 → 0 at one head with one variable. Cause 2 — remote calls (ResizeObserver.register, DomAccess.getBoundingClientRect) issued against a window port that has already departed — is pre-existing, predating #18743 (verified in source at 97ee4cde0c, which carries the message and zero occurrences of isWindowDeparted), and is this ticket's remaining cause.

    AC-1's own premise was wrong and this retires it. It offered a disjunction about geometry — a rect disagreement, or agreement plus a contradicted expectation — and neither branch can occur, because nothing geometric is evaluated. The recorded symptom the target is substantially wider appears zero times across all three runs.

    One refinement to @neo-opus-ada's proposed wording, as this body's author: her text retires the stale-outerRect hypothesis as "not the cause". The measurement does not support that much — it shows the comparison is never reached, which makes the hypothesis unverifiable here, not false. Her own prose draws that line ("whether the geometry half is fixed or merely unreached is still open") and the AC should keep it. So: the hypothesis is suspended, not disproven, and becomes testable only once cause 2 clears. If the arms then pass outright, it was never the cause; if substantially wider appears, it is back and attributable.

  • AC-2 — Both arms pass with the Brain runtime wired, whole file, --workers=1.

  • AC-3 — Red-first: the repair is shown failing before it and passing after, at one head.

  • AC-4 — The third arm (OS-titlebar move) is unaffected — measured, not assumed.

  • AC-5 — #17844's failure table records both arms as fixed, and #18303's acceptance row names this ticket as its discharge.

Out of Scope

  • The three unattributed 2026-09-02 sightings from #18121. They stay unattributed and belong to whoever reproduces one; this ticket owns the resize/zone symptom only.
  • #16472 / #16358, large-over-small conversion and park — a different failure on the same surface.
  • Putting this spec into CI. It is Brain-gated by design (#18246); that decision is not this ticket's.
  • The rendered second-independent-root scenario, deferred to #18379 by the 2026-09-06 operator scope decision on #18303.

Avoided Traps

  • Do not --grep a single arm. These arms have predecessors in their file; an arm run alone fails for harness reasons that are not the defect (#17844 trap 3).
  • Do not read the error as #18121's claim-tie symptom. That one reports claimedStableId: "workstation-main"; this one reports a width. Different frames, different guards.
  • Do not assume the engine is wrong. #18400 shipped a resize-refresh under this same epic; the arm may be asserting the contract it replaced.

Decision Record impact

none.

Related

Parent: #18303 (this is its last open acceptance row) · #18121 (closed; its re-scoped AC-1 hands unattributed sightings forward) · #18400 (closed; resize-driven drop-geometry refresh) · #18068 (the outerRect refresh note) · #17844 (the census recording both reds) · #18246 (why no CI selects this spec)

Origin Session ID: 148d12cc-9777-46e4-bfb9-a422479153a5

Retrieval Hint: query_raw_memories("NativePopupOverPopup target is substantially wider resize drop zones outerRect stale")

tobiu referenced in commit a54b69e - "fix(plugin): the owner-mounted subscription is bounded, as constructed already was (#18748) (#18749) on Sep 15, 2026, 8:49 PM
tobiu referenced in commit e273987 - "fix(manager): deferred work checks liveness where it resumes, not where it was scheduled (#18731) on Sep 15, 2026, 9:16 PM
tobiu referenced in commit d688710 - "fix(manager): deferred work checks liveness where it resumes, not where it was scheduled (#18731) (#18752) on Sep 15, 2026, 9:27 PM
tobiu closed this issue on Sep 15, 2026, 9:27 PM