LearnNewsExamplesServices
Frontmatter
id19693
titleOverflow.project() reads a destroyed owner as live and logs its teardown as a defect
stateClosed
labels
bugaitesting
assigneesneo-opus-grace
createdAtOct 13, 2026, 12:40 AM
updatedAtOct 13, 2026, 1:25 AM
githubUrlhttps://github.com/neomjs/neo/issues/19693
authorneo-opus-grace
commentsCount0
parentIssue19655
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtOct 13, 2026, 1:25 AM

Overflow.project() reads a destroyed owner as live and logs its teardown as a defect

Closed Backlog/active-chunk-8 bugaitesting
neo-opus-grace
neo-opus-grace commented on Oct 13, 2026, 12:40 AM

Context

During a Workstation pop-out, the App worker logs Neo.tab.plugin.Overflow: project() threw against a live owner Symbol(Neo.isDestroyed) (src/tab/plugin/Overflow.mjs, the project() catch). The workerErrors auto-fixture (test/playwright/fixtures.mjs:138) fails any test whose page console receives it.

Observed:

  • WorkstationPopOutPaneIdentityNL.spec.mjs:205 was red on both attempts of the retry in dev run 38313618683. It failed 1/3 on a hosted check (run 38315848558) with this exact error.
  • WorkstationReopenedPopupReturnNL.spec.mjs:125-128 already allowlists the same line as defect observation a93f3f742aff4b33. It was never promoted to a ticket. This is the second spec it fails, which meets the promotion rule.
  • A hosted probe logged state from the catch, and it failed 1 of 6. At that failure, the owner neo-tab-header-toolbar-9 is destroyed, and so is the plugin. The only measured button is destroyed too, and there is no control. The owner was not live: it was destroyed and still read mounted: true.

The Problem

The catch decides "live owner" from owner.mounted && !isWindowDeparted(owner.windowId). Destruction does not reset mounted, so a destroyed owner passes that test. Its own teardown, the projection rejecting with Neo.isDestroyed, is then logged as a programming defect. Every journey that destroys a tab header mid-projection either fails or must allowlist the line.

The Architectural Reality

  • The catch in src/tab/plugin/Overflow.mjs project() asks about two forms of departure: the owner unmounting, and its window retiring its port. A third, the owner or the plugin being destroyed, is not asked about. The finally releases the latch.
  • core.Base#destroy rejects every trapped await with Neo.isDestroyed, and component.Base#destroy settles parked render promises the same way.

Design authority: the catch's own comment. A throw against a live owner is a defect, and a teardown race is the expected failure. A destroyed owner or plugin is torn down, not live.

The Fix

The catch's live-owner test also requires that neither the plugin nor its owner is destroyed. Every other throw against a mounted, undestroyed owner in an undeparted window is still logged. Remove the allowlist in WorkstationReopenedPopupReturnNL.spec.mjs.

Acceptance Criteria

  • A receipt names the destroyed instances: the owner and the plugin (run 38318164876).
  • A unit arm in test/playwright/unit/tab/plugin/Overflow.spec.mjs, red before the fix, proves both directions. A projection that throws after its owner or plugin is destroyed logs nothing, even though mounted still reads true. A throw against an undestroyed, mounted owner is still logged.
  • The allowlist line in WorkstationReopenedPopupReturnNL.spec.mjs is removed, and that spec passes on a hosted run.
  • Post-merge: WorkstationPopOutPaneIdentityNL:205 passes in the next ten-shard dev run.

Out of Scope

  • The other allowlisted line in that spec, CanvasGroups: window … belongs to no canvas group.
  • Whether destroy() should reset mounted. That is an engine-wide change; this catch must not depend on it.
  • The rest of #19657's intermittent set.

Related

#19657, epic #19655, #19583 (where the allowlist was added).

Live latest-open sweep: the latest open issues at 2026-10-12T22:40Z, plus gh search issues for the observation id and the error text. No equivalent found. The A2A defect note from this session is the capture.

Origin Session ID: 02040656-932a-48c8-8894-6f43d0368663

tobiu referenced in commit 26786be - "fix(tab): Overflow.project() treats a destroyed owner or plugin as teardown, not a live-owner defect (#19693) (#19701)" on Oct 13, 2026, 1:25 AM
tobiu closed this issue on Oct 13, 2026, 1:25 AM