The #17844 battery at dev@e66b6f8142 (census) has five red arms with one shape. A window closes. Teardown code that nobody awaits then calls into it. The call rejects with NEO_DEAD_PORT, and the App Worker mirrors the unhandled rejection to the page, where #18716's gate ("a worker or page error nobody named fails its test", 2026-09-15) fails the test.
The rejections are not new: NEO_DEAD_PORT has been typed since #17896 (08-31). They were silent until the gate. Three of the five are the "inherited" reds @neo-gpt-emmy baseline-reproduced for #19054 (receipt).
The Problem
After the window left, these calls reject and nobody handles them:
Arms (by title, dev@e66b6f8142): WindowOps › operates one exact popup generation…, WorkstationTopologyGroupsNL › warm F5 preserves the Workspace… and › A and its pop-out share one Group…, WorkstationReclaimEmbodimentNL › control: a vessel that connected and then closed…, SharedWorkerRemoteReplay › a second window and a reloaded window….
The Architectural Reality
worker.Base#promiseMessage (src/worker/Base.mjs:673-696) rejects a send with no live port as code: 'NEO_DEAD_PORT'. The error message carries the destination, which for window-bound remotes is the windowId.
worker.Base#isWindowDeparted (:415, #18741) answers at the instant of rejection, because removePort() records the departure before onDisconnect() fires. It is bounded to recent departures.
The unhandledrejection listener (:314-316) mirrors every rejection that is not defaultPrevented. Its comment states the current contract: Neo.mjs marks the rejections it expects as handled.
Six sites mark their rejections today, using three different predicates:
checked before the call:manager/DomEvent.mjs:353, tab/plugin/Overflow.mjs:862 (#18741)
The Fix
Fork, open with @neo-opus-ada (#18716 / #18741 are her surface):
A — settle at the unhandled boundary (recommended).promiseMessage attaches destination to the NEO_DEAD_PORT rejection. The unhandledrejection listener drops a rejection only when it is NEO_DEAD_PORTandisWindowDeparted(destination) is true.
Awaited or caught calls still receive their rejection.
A never-known or still-live destination is still mirrored, so Overflow's rule that "a throw against a live owner is a defect" holds.
Per-site copies that exist only for this settlement become deletable.
B — per site. Apply the both-halves predicate at the nine new sites, as #19011 did. This is the precedent, and it is the fifteenth copy of one rule.
Falsifier for A: a teardown caller that must observe a departed-window rejection without handling it. None is known; any site that needs it already handles it, so A never reaches it.
A: a NEO_DEAD_PORT rejection to a departed destination is not mirrored; everything else unchanged
Handled and awaited rejections unchanged
The listener's comment and promiseMessage JSDoc
Unit arms with a mutation control; the five battery arms
NEO_DEAD_PORT rejection
promiseMessage
Gains destination (additive)
The message string is unchanged
promiseMessage JSDoc
Unit arm reads the field
Acceptance Criteria
AC-1 — The fork is recorded on this ticket before implementation merges (A or B, with the reason). 2026-09-22: A, approved by @neo-opus-ada (owner of #18716 / #18741) at 22:49Z. Her two conditions: settled drops stay countable (settledDepartureCount), and only per-site copies that exist solely for this settlement are deleted. None of the six does, so all stay (see AC-4).
AC-2 — Unit arms cover four cases:
an unawaited call to a destination that departed produces no mirrored error;
the same rejection to a never-known destination is mirrored;
a live destination's failure is mirrored;
a caught call still receives its rejection.
Disabling the settlement turns the first arm red.
AC-3 — The four arms that fail on this class pass on the merge head, run as whole files with one worker and NEO_AGENTOS_RUNTIME_ROOT wired: WindowOps, SharedWorkerRemoteReplay, WorkstationReclaimEmbodimentNL › control: a vessel that connected and then closed… and WorkstationTopologyGroupsNL › A and its pop-out share one Group…. Amended 2026-09-22: the fifth arm, warm F5, still fails once this class is settled, on a different cause, and moved to #19060.
AC-4 — Under A, per-site handling whose only purpose is this settlement is removed, or kept with a named reason. Under B, every listed site uses the both-halves predicate, not the code-only one.
Out of Scope
The main-thread Target worker … does not exist (src/worker/Manager.mjs:612, DockCrossWindowWitnessNL): a different transport.
Neo.isDestroyed rejections.
Specs that provoke an error on purpose and do not name it for the gate (CreateInstance, the two WriteGuard specs).
Avoided Traps
A blanket catch at each call site. It hides live-owner defects; #18741 rejected it for that reason.
Code-only discrimination.NEO_DEAD_PORT means "unreachable", not "departed"; a port that never existed carries it too.
Decision Record impact
None. The policy stays inside worker.Base and reuses #18741's predicate.
Sweeps: live latest-open at 2026-09-22T22:45Z and 22:47Z, no equivalent. A2A claims in the last 30 messages: none on this class. Memory Core: #18731/#18741 prior design (per-site predicate; blanket catch rejected). Own assignments: none overlapping.
tobiu referenced in commit a186e92 - "fix(worker): a teardown call into a departed window is settled, not mirrored as a defect (#19056) (#19061) on Sep 23, 2026, 2:15 AM
Context
The #17844 battery at
dev@e66b6f8142(census) has five red arms with one shape. A window closes. Teardown code that nobody awaits then calls into it. The call rejects withNEO_DEAD_PORT, and the App Worker mirrors the unhandled rejection to the page, where #18716's gate ("a worker or page error nobody named fails its test", 2026-09-15) fails the test.The rejections are not new:
NEO_DEAD_PORThas been typed since #17896 (08-31). They were silent until the gate. Three of the five are the "inherited" reds @neo-gpt-emmy baseline-reproduced for #19054 (receipt).The Problem
After the window left, these calls reject and nobody handles them:
ScrollSync.unregister,grid/VerticalScrollbar.mjs:95ResizeObserver.unregister,grid/Container.mjs:364and:1054GridDragScroll/GridRowScrollPinning/GridHorizontalScrollSyncunregisters,grid/ScrollManager.mjs:285,:307,:327,:357DomAccess.transferCanvasToWorker,worker.Manager.startWorker,DomAccess.getLayoutRectArms (by title,
dev@e66b6f8142):WindowOps › operates one exact popup generation…,WorkstationTopologyGroupsNL › warm F5 preserves the Workspace…and› A and its pop-out share one Group…,WorkstationReclaimEmbodimentNL › control: a vessel that connected and then closed…,SharedWorkerRemoteReplay › a second window and a reloaded window….The Architectural Reality
worker.Base#promiseMessage(src/worker/Base.mjs:673-696) rejects a send with no live port ascode: 'NEO_DEAD_PORT'. The error message carries the destination, which for window-bound remotes is thewindowId.worker.Base#isWindowDeparted(:415, #18741) answers at the instant of rejection, becauseremovePort()records the departure beforeonDisconnect()fires. It is bounded to recent departures.unhandledrejectionlistener (:314-316) mirrors every rejection that is notdefaultPrevented. Its comment states the current contract:Neo.mjsmarks the rejections it expects as handled.container/Viewport.mjs:144(#19011)draggable/DragZone.mjs:445,grid/Body.mjs:1321,table/Body.mjs:631manager/DomEvent.mjs:353,tab/plugin/Overflow.mjs:862(#18741)The Fix
Fork, open with @neo-opus-ada (#18716 / #18741 are her surface):
promiseMessageattachesdestinationto theNEO_DEAD_PORTrejection. Theunhandledrejectionlistener drops a rejection only when it isNEO_DEAD_PORTandisWindowDeparted(destination)is true.Falsifier for A: a teardown caller that must observe a departed-window rejection without handling it. None is known; any site that needs it already handles it, so A never reaches it.
Contract Ledger Matrix
worker.Baseunhandled-rejection mirrorNEO_DEAD_PORTrejection to a departed destination is not mirrored; everything else unchangedpromiseMessageJSDocNEO_DEAD_PORTrejectionpromiseMessagedestination(additive)promiseMessageJSDocAcceptance Criteria
AC-1 — The fork is recorded on this ticket before implementation merges (A or B, with the reason). 2026-09-22: A, approved by @neo-opus-ada (owner of #18716 / #18741) at 22:49Z. Her two conditions: settled drops stay countable (
settledDepartureCount), and only per-site copies that exist solely for this settlement are deleted. None of the six does, so all stay (see AC-4).AC-2 — Unit arms cover four cases:
Disabling the settlement turns the first arm red.
AC-3 — The four arms that fail on this class pass on the merge head, run as whole files with one worker and
NEO_AGENTOS_RUNTIME_ROOTwired:WindowOps,SharedWorkerRemoteReplay,WorkstationReclaimEmbodimentNL › control: a vessel that connected and then closed…andWorkstationTopologyGroupsNL › A and its pop-out share one Group…. Amended 2026-09-22: the fifth arm, warm F5, still fails once this class is settled, on a different cause, and moved to #19060.AC-4 — Under A, per-site handling whose only purpose is this settlement is removed, or kept with a named reason. Under B, every listed site uses the both-halves predicate, not the code-only one.
Out of Scope
Target worker … does not exist(src/worker/Manager.mjs:612,DockCrossWindowWitnessNL): a different transport.Neo.isDestroyedrejections.CreateInstance, the two WriteGuard specs).Avoided Traps
NEO_DEAD_PORTmeans "unreachable", not "departed"; a port that never existed carries it too.Decision Record impact
None. The policy stays inside
worker.Baseand reuses #18741's predicate.Related
Sweeps: live latest-open at 2026-09-22T22:45Z and 22:47Z, no equivalent. A2A claims in the last 30 messages: none on this class. Memory Core: #18731/#18741 prior design (per-site predicate; blanket catch rejected). Own assignments: none overlapping.
Origin Session ID: 19051047-2f38-4bf0-be51-efd6b957564d
Retrieval Hint:
query_raw_memories("NEO_DEAD_PORT departed window teardown unhandled rejection isWindowDeparted unregister")