Context
The operator challenged the architectural shape of src/dashboard/dock/window/TearOut.mjs and VesselEmbodiment.mjs after merging the bounded drag repair in #19539. This records the inherited debt separately from that behavior fix.
Source examined: 5698517f193fb1064a5aa618ff6741bee946fae6. The same problem was already repaired for WorkspaceSet in #18450, whose scope explicitly excluded TearOut.
The Problem
TearOut.mjs:108 exports a factory accepting 15 callbacks and retaining vessel/admission/retirement state and pane maps in a closure. Internal operations such as invalidateAdmission (:158), retireVessel (:174) and livePane (:226) are lexical functions. The returned methods close over those functions instead of participating in Neo prototype dispatch.
VesselEmbodiment.mjs:26-40 creates a private records Map and lexical stage; its second factory, createDockVesselProxyEmbodiment (:229), creates another state machine with private active/generation state and lexical retireProxy / resolveRecord. Creating Neo components inside those functions does not make their owner a Neo class.
NativeVesselTransaction.mjs:431-486 is a second form of the same problem inside a registered class: effectsFor(descriptor) returns the reshowVessel closure, the one effect left after #19538 (PR #19542). Its body calls the class statics by name (NativeVesselTransaction.toFrameOrigin, .resolveAdmissions, .describeAuthority), so a subclass cannot replace them. Namespace registration does not put the effect body on the class prototype. The one caller, Workstation VesselWorkspace.mjs:300, names the class at its call site; Demo-B no longer constructs it.
Workspace.mjs:690 wires the first factory, :1674 spreads its returned object into projection options, and :1587 tears down its records by hand. Demo-B wires the same factories itself (DemoBWorkspace.mjs:519, :620, :633). TearOut explicitly justifies closure calls because spreading changes this.
An exact-source Node probe confirmed TearOut returns an ordinary Object, each construction owns different handler functions, and the module has neither Neo.setupClass nor Neo.gatekeep. Passing behavior tests does not establish namespace, inheritance, overwrite dispatch or native lifetime integration.
The Architectural Reality
Design authority: src/core/Base.mjs defines itself as “The base class for (almost) all classes inside the Neo namespace”; src/Neo.mjs#create and setupClass provide construction, prototype configuration/overwrites and namespace registration. #18450 establishes the relevant extension/lifetime precedent; src/dashboard/dock/window/WorkspaceSet.mjs is the live neighboring Base-derived class.
This concerns stateful Engine collaborators, not a ban on ordinary pure functions. Pane/component identity, native-resource ownership and Group/document authority remain separate. A class conversion must not take ownership of borrowed panes or Group participants.
Ownership constraints from Ada's peer read: TearOut releases borrowed pane handles on destruction; the host's settlePane remains the pane-disposal decision. Native windows retire through the Group's native lifecycle and existing host close seam, never through an independent destructor close. Documents remain participant-owned. Embodiment owns its created placeholders/proxies; staged panes remain borrowed and follow restore/promote. Generation fencing remains instance-local.
Placement: retain the existing src/dashboard/dock/window/ concern and its registered-class sibling pattern; no new subsystem or manager. The Engine has no ai:structure-map script (the explicit command returned Missing script); this is not a passed structure-map check.
The Fix
Replace the surviving stateful factory implementations in TearOut and VesselEmbodiment, and the surviving factory-generated native effects in NativeVesselTransaction.effectsFor, with real Neo collaborators: behavior on prototype methods, deliberate configs versus per-instance fields, standard construction and creator-owned destruction. Make internal calls dispatch through the instance so subclass and overwrite behavior actually executes.
Update production callers and existing tests together; remove redundant callback forwarding and object-spread plumbing where the native collaboration replaces it. Retain genuine host policy boundaries. Do not preserve the factory as an implementation hidden behind a registered facade.
First reconcile the surviving call paths with Ada-owned #19538: delete retired pointer-conversion machinery instead of converting dead code. That cleanup remains its owner's lane. Preserve the accepted repeated tear-out / target-proxy / re-exit behavior and the separate native-titlebar path.
Contract Ledger
| Target surface |
Source of authority |
Proposed behavior |
Fallback |
Docs |
Evidence |
| TearOut factory and handlers |
current TearOut.mjs; Neo.create/setupClass; #18450 |
actual registered collaborator with executed prototype methods |
retain current admission/refusal results |
class/config/method JSDoc |
subclass and Neo.overwrites change executed behavior |
| Surviving embodiment factories |
current VesselEmbodiment.mjs; same Base contract |
registered, explicitly owned render-state collaborator(s) |
preserve restore/promote and stale-generation refusal |
class/ownership JSDoc |
real dispatch plus same-pane/placeholder controls |
| Surviving native effect bundle |
NativeVesselTransaction.effectsFor; the Workstation caller; #19538 |
move executed effects to overridable prototype methods of their explicit owner |
preserve native route/restore/refusal behavior; remove dead effects first |
replace factory-oriented JSDoc |
override reaches the surviving native effect; no namespace-only false green |
| Construction and teardown |
Workspace constructor/destructor and verified callers |
creators release their instances; borrowed resources keep their authority |
late work cannot revive a destroyed owner |
lifecycle JSDoc |
destroy during pending work; no lost panes or foreign teardown |
| Drag/document behavior |
merged #19539; docking design record |
unchanged observable semantics |
existing blocked-open/cancel/re-entry behavior |
existing relevant guides |
focused units and exact-head Workstation/Demo-B journeys |
Decision Record impact
Aligned with the existing Neo class system and Dock ownership direction; no new Group protocol, persisted schema or user interaction policy. Re-read the current docking design record at implementation.
Acceptance Criteria
Out of Scope
A wholesale Dock rewrite, feature changes, weakening release acceptance, another generic abstraction layer, or taking over #19538. This ticket does not claim all remaining Dock modules have been audited.
Avoided Traps
A class wrapper around copied factory methods still defeats overrides (#18450). A blanket PascalCase-to-class rule can misclassify pure data helpers. Base destruction alone does not settle native-resource ownership. Green tests over injected callbacks do not prove extensibility.
Related and ownership
Related: #18450, #18304, #19538, #19539, neomjs/neo-agent-skills#155.
Owner: @neo-opus-ada self-selected and is assigned. Sequence: #19538 deletion first, then #19540 conversion of the surviving behavior; one owner coordinates the shared Workstation/Demo-B host wiring. The deletion landed (PR #19542, 2026-10-10 13:43Z). The caller census, re-run at 7c4b2273b8, finds all TearOut behavior and both embodiment factories surviving, and effectsFor reduced to the native title-bar re-show. Emmy retains review/finding custody.
MC prior art: memory c93f166c-c155-4463-8ed7-0d5e993aa332 records the original injected admission-machine rationale; it explains the history, not architectural approval. Live all-state issue searches found the closed WorkspaceSet precedent and no equivalent open repair. Own-assignment sweep found only unrelated Engine lanes. Latest-open and all-state mailbox sweeps are repeated immediately before creation.
Origin Session ID: b56dbc41-6e95-4210-a2ea-8d1f5f3ffcd0
Retrieval Hint: TearOut VesselEmbodiment closure factory prototype override; source 5698517f193fb1064a5aa618ff6741bee946fae6.
Context
The operator challenged the architectural shape of
src/dashboard/dock/window/TearOut.mjsandVesselEmbodiment.mjsafter merging the bounded drag repair in #19539. This records the inherited debt separately from that behavior fix.Source examined:
5698517f193fb1064a5aa618ff6741bee946fae6. The same problem was already repaired forWorkspaceSetin #18450, whose scope explicitly excluded TearOut.The Problem
TearOut.mjs:108exports a factory accepting 15 callbacks and retaining vessel/admission/retirement state and pane maps in a closure. Internal operations such asinvalidateAdmission(:158),retireVessel(:174) andlivePane(:226) are lexical functions. The returned methods close over those functions instead of participating in Neo prototype dispatch.VesselEmbodiment.mjs:26-40creates a private records Map and lexicalstage; its second factory,createDockVesselProxyEmbodiment(:229), creates another state machine with private active/generation state and lexicalretireProxy/resolveRecord. Creating Neo components inside those functions does not make their owner a Neo class.NativeVesselTransaction.mjs:431-486is a second form of the same problem inside a registered class:effectsFor(descriptor)returns thereshowVesselclosure, the one effect left after #19538 (PR #19542). Its body calls the class statics by name (NativeVesselTransaction.toFrameOrigin,.resolveAdmissions,.describeAuthority), so a subclass cannot replace them. Namespace registration does not put the effect body on the class prototype. The one caller, WorkstationVesselWorkspace.mjs:300, names the class at its call site; Demo-B no longer constructs it.Workspace.mjs:690wires the first factory,:1674spreads its returned object into projection options, and:1587tears down its records by hand. Demo-B wires the same factories itself (DemoBWorkspace.mjs:519,:620,:633). TearOut explicitly justifies closure calls because spreading changesthis.An exact-source Node probe confirmed TearOut returns an ordinary Object, each construction owns different handler functions, and the module has neither
Neo.setupClassnorNeo.gatekeep. Passing behavior tests does not establish namespace, inheritance, overwrite dispatch or native lifetime integration.The Architectural Reality
Design authority:
src/core/Base.mjsdefines itself as “The base class for (almost) all classes inside the Neo namespace”;src/Neo.mjs#createandsetupClassprovide construction, prototype configuration/overwrites and namespace registration. #18450 establishes the relevant extension/lifetime precedent;src/dashboard/dock/window/WorkspaceSet.mjsis the live neighboring Base-derived class.This concerns stateful Engine collaborators, not a ban on ordinary pure functions. Pane/component identity, native-resource ownership and Group/document authority remain separate. A class conversion must not take ownership of borrowed panes or Group participants.
Ownership constraints from Ada's peer read: TearOut releases borrowed pane handles on destruction; the host's
settlePaneremains the pane-disposal decision. Native windows retire through the Group's native lifecycle and existing host close seam, never through an independent destructor close. Documents remain participant-owned. Embodiment owns its created placeholders/proxies; staged panes remain borrowed and follow restore/promote. Generation fencing remains instance-local.Placement: retain the existing
src/dashboard/dock/window/concern and its registered-class sibling pattern; no new subsystem or manager. The Engine has noai:structure-mapscript (the explicit command returned Missing script); this is not a passed structure-map check.The Fix
Replace the surviving stateful factory implementations in TearOut and VesselEmbodiment, and the surviving factory-generated native effects in
NativeVesselTransaction.effectsFor, with real Neo collaborators: behavior on prototype methods, deliberate configs versus per-instance fields, standard construction and creator-owned destruction. Make internal calls dispatch through the instance so subclass and overwrite behavior actually executes.Update production callers and existing tests together; remove redundant callback forwarding and object-spread plumbing where the native collaboration replaces it. Retain genuine host policy boundaries. Do not preserve the factory as an implementation hidden behind a registered facade.
First reconcile the surviving call paths with Ada-owned #19538: delete retired pointer-conversion machinery instead of converting dead code. That cleanup remains its owner's lane. Preserve the accepted repeated tear-out / target-proxy / re-exit behavior and the separate native-titlebar path.
Contract Ledger
TearOut.mjs;Neo.create/setupClass; #18450Neo.overwriteschange executed behaviorVesselEmbodiment.mjs; same Base contractNativeVesselTransaction.effectsFor; the Workstation caller; #19538Decision Record impact
Aligned with the existing Neo class system and Dock ownership direction; no new Group protocol, persisted schema or user interaction policy. Re-read the current docking design record at implementation.
Acceptance Criteria
Neo.overwritesoverride reach real internal behavior, including the surviving native effects, rather than only checking namespace presence.Out of Scope
A wholesale Dock rewrite, feature changes, weakening release acceptance, another generic abstraction layer, or taking over #19538. This ticket does not claim all remaining Dock modules have been audited.
Avoided Traps
A class wrapper around copied factory methods still defeats overrides (#18450). A blanket PascalCase-to-class rule can misclassify pure data helpers. Base destruction alone does not settle native-resource ownership. Green tests over injected callbacks do not prove extensibility.
Related and ownership
Related: #18450, #18304, #19538, #19539, neomjs/neo-agent-skills#155. Owner: @neo-opus-ada self-selected and is assigned. Sequence: #19538 deletion first, then #19540 conversion of the surviving behavior; one owner coordinates the shared Workstation/Demo-B host wiring. The deletion landed (PR #19542, 2026-10-10 13:43Z). The caller census, re-run at
7c4b2273b8, finds all TearOut behavior and both embodiment factories surviving, andeffectsForreduced to the native title-bar re-show. Emmy retains review/finding custody.MC prior art: memory
c93f166c-c155-4463-8ed7-0d5e993aa332records the original injected admission-machine rationale; it explains the history, not architectural approval. Live all-state issue searches found the closed WorkspaceSet precedent and no equivalent open repair. Own-assignment sweep found only unrelated Engine lanes. Latest-open and all-state mailbox sweeps are repeated immediately before creation.Origin Session ID: b56dbc41-6e95-4210-a2ea-8d1f5f3ffcd0 Retrieval Hint: TearOut VesselEmbodiment closure factory prototype override; source 5698517f193fb1064a5aa618ff6741bee946fae6.