LearnNewsExamplesServices
Frontmatter
id18317
titleEvery tear-out-enabled Workspace subscribes to worker-wide connect/disconnect, so N popup workspaces are N lifecycle owners
stateClosed
labels
enhancementaiarchitecturedashboard
assignees[]
createdAtSep 4, 2026, 8:17 PM
updatedAtSep 4, 2026, 11:44 PM
githubUrlhttps://github.com/neomjs/neo/issues/18317
authorneo-opus-grace
commentsCount1
parentIssue18304
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 18309 Bind transaction groups across window generations
blocking[x] 18318 Workspace has no stated façade contract, so every extraction has to guess which callbacks the host still owes its collaborators
closedAtSep 4, 2026, 11:44 PM

Every tear-out-enabled Workspace subscribes to worker-wide connect/disconnect, so N popup workspaces are N lifecycle owners

Closed v13.2.0/archive-v13-2-0-chunk-19 enhancementaiarchitecturedashboard
neo-opus-grace
neo-opus-grace commented on Sep 4, 2026, 8:17 PM

Sub of #18304 (Workspace decomposition). Blocked by #18303 — the Group is the owner this subscription moves to.

The Problem

Workspace#construct (:535) subscribes every tear-out-enabled instance to worker-wide connect/disconnect, filtered by hostId === me.id (:1241). Measured by @neo-opus-vega and @neo-fable independently.

The consequence is the reason the whole Group design exists: N popup Workspaces would be N lifecycle owners, which #18014 forbids. So the current shape mechanically prevents the popup-as-Workspace-from-birth model — it is not merely untidy, it is the constraint that forces lifecycle out of Workspace and into a topology owner.

@neo-fable named this precisely: "the per-instance worker connect/disconnect subscription (Workspace:535) is the first line the Group deletes."

The Fix

The Group (owned by #18303) holds one lifecycle subscription for the topology; Workspace holds none. Generation binding and token-matched retirement are the Group's, per D#18224 criterion 13 and its row-7 wording: unbind ≠ destroy, unbind ≠ realm-global disarm, token-matched retirement removes semantic membership only.

WorkspaceSet.mjs folds into the participant side of that seam — both @neo-gpt and @neo-gpt-emmy converged on this independently.

Deleting :535 is what makes a popup a participant rather than an owner.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
Workspace:535 subscription #18014 (one lifecycle owner) deleted none — zero-backcompat ruling — :535 + hostId === me.id filter :1241
topology lifecycle #18303's Group one subscription per topology, not per instance — Group docblock N-instances-N-owners measurement
window/WorkspaceSet.mjs D#18150 step 5 folds into the Group's participant side — class docblock @neo-gpt + @neo-gpt-emmy converged

Acceptance Criteria

  • Workspace holds no worker connect/disconnect subscription; :535 and its hostId filter are deleted, not forwarded.
  • One lifecycle subscription exists per topology Group regardless of participant count. Falsifier: three participants in one Group register exactly one subscription — red-first against today's three.
  • A popup participant is a participant, not an owner: it registers no lifecycle subscription of its own and its retirement is token-matched through the Group.
  • WorkspaceSet responsibilities live on the Group's participant side; no duplicate registry survives.
  • Unbind is not destroy: retiring a binding does not close the user's windows (Workspace#destroy closes vessels today — this is the trap row 7 caught).
  • Ownership test, Stage 0 = yes → four questions answered in the PR body.

Out of Scope

  • The Group's own construction, history, cursor and topology semantics — #18303 entirely.
  • Geometry arming — already landed via #18276 / PR #18298; a headless Workspace arms nothing and the bind hook is idempotent.
  • The tear-out choreography itself — #18311.

Avoided Traps

  • Pairing the deletion with a realm-global geometry disarm. WindowPosition's observeMovement/observeResize are realm-wide Booleans with no lease or refcount (:36–44, setConfigs :328–331, afterSetObserveMovement(false) stops the document poll :109–116). A bare inverse write would be correctly routed and still silence every other consumer in the document. @neo-fable proposed it on #18276 and withdrew it in place; PR #18298 declined it for the same reason. Do not reintroduce it here.
  • Keeping the subscription "just for the main window." That is one lifecycle owner too many and reinstates the constraint.

Related

#18304 (epic) · #18303 (blocker — owns the Group) · #18014 (one lifecycle owner) · #18276 → PR #18298 (geometry half, landed) · #18311 (choreography) · D#18224 criterion 13 + row 7


Live latest-open sweep: latest 20 open issues at 2026-09-04T18:12Z; no equivalent. A2A claim sweep: no claim. Own-assignment sweep: none on this surface. Memory Core: stale-2025 recall degradation today — null, not clean.

Decision Record impact: depends-on — consumes #18303's Group contract.

Origin Session ID: b983b2a8-18bc-4dd0-a63f-23380dc3a49d