LearnNewsExamplesServices
Frontmatter
id18309
titleBind transaction groups across window generations
stateClosed
labels
enhancementaiarchitecturecoredashboard
assigneesneo-opus-vega
createdAtSep 4, 2026, 8:17 PM
updatedAtSep 5, 2026, 3:03 PM
githubUrlhttps://github.com/neomjs/neo/issues/18309
authorneo-gpt
commentsCount7
parentIssue18303
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 18308 Hard-cut dock topology persistence to keyed workspace records
blocking[x] 18311 window/TearOut.mjs has been imported for months while its ~280 lines of pane-handle choreography stayed in Workspace, [x] 18317 Every tear-out-enabled Workspace subscribes to worker-wide connect/disconnect, so N popup workspaces are N lifecycle owners, [x] 18316 Add Workstation multi-window undo, redo, save, and reload, [x] 18272 An N-slot topology commit restores N documents and one view: no consumer has a sibling projection to converge, [x] 18284 Two independent Workstation roots share one drag namespace, so a cross-root drag commits into the wrong document, [x] 18315 Hydrate cold dock topologies and rebind warm reloads, [x] 18314 Unify Dock and Neural Link undo on the group cursor, [x] 18313 Record observed popup placement as a transaction participant, [x] 18312 Commit multi-workspace dock transactions atomically, [x] 18310 Add immutable per-group transaction history and cursor
closedAtSep 5, 2026, 3:03 PM

Bind transaction groups across window generations

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

Context

A warm reload produces a new windowId while the App Worker heap, Workspace instances, and topology may survive. Independent roots of the same app can share one SharedWorker, so appName cannot identify a transaction authority. D#18224 resolved four separate coordinates and a token-matched binding lifecycle.

The Problem

Today Neo.appsByName[appName] is an array and fallback lookup selects [0]; same-name generations can coexist after reload. Popup/workspace state is connected by consumer-specific URLs and lifecycle hooks. A late disconnect can therefore target the wrong generation, and two independent Workstation roots can collapse into one identity.

The Architectural Reality

Neo.manager.Base is already a collection. A generic Neo.manager.Transaction registry matches manager.Store / manager.Window; its Group children must remain dock-blind. Dock Workspaces register as participants through domain adapters. appName stays routing metadata.

The Fix

Add the collection-backed singleton registry plus generic Group identity/binding lifecycle. Main-thread boot injects a root-minted groupId; popups inherit it and carry one workspaceKey and spawn claimToken; windowId is only the replaceable runtime generation. Bindings are (groupId, workspaceKey) -> {windowId, generationToken} and unregister only on an exact token match. Duplicate live roots fork; stale popup URLs fail closed.

This increment also moves semantic participant membership into the Group. The existing dock-side WorkspaceSet may remain as a domain adapter, but it owns no independent entry map. Its adoption/compensation behavior stays intact until #18312 moves the write protocol; releasing a window binding does not retire its semantic participant.

Structural Placement

Full structural pre-flight selected src/manager/Transaction.mjs plus src/manager/transaction/ for generic Group support; dock adapters stay under src/dashboard/dock/. Update the Runtime Engine inventory because it currently has no src/manager/ row. No dock imports enter the manager.

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
manager.Transaction manager sibling pattern; #18303 collection-backed Group registry unknown group returns null/refusal Architecture Overview + JSDoc registry unit arms
boot identity carrier D#18224 criteria 6/13 root mints/injects; popup inherits absent/stale carrier becomes explicit new-root or root fallback ADR + boot JSDoc four-way carrier matrix
semantic binding D#18224 criterion 6 token-matched generation replacement duplicate live binder forks class JSDoc late-disconnect red control
participant membership / WorkspaceSet adapter #18303; author boundary disposition Group owns the single registry keyed by workspaceKey; the dock adapter delegates membership and lookup window unbind/lease expiry preserves registered semantic participants Group/adapter JSDoc same registry observed through Group and adapter; headless participant survives binding expiry
app identity Application / appsByName reality appName routes only never implicit Group identity ADR two same-app roots

Decision Record impact

Depends on and implements amended ADR 0029.

Decision Record

Required prerequisite: the #18303 hard-cut ADR leaf.

Discussion Criteria Mapping

Owns criterion 6 and the binding half of criterion 13; provides criterion 3's first manager ownership cut under the bounded accounting amendment below.

Acceptance Criteria

  • manager.Transaction registers distinct logical Groups and is exported through the normal manager surface.
  • Root reload retains groupId, popup birth/reload inherits it, and an independent same-app root receives a different id.
  • One workspaceKey identifies each full Workspace slot; no parallel workspaceId/window-slot identity survives without a proven non-1:1 consumer. Group owns semantic participant membership in this increment; any surviving WorkspaceSet delegates to that registry and has no independent entry map. Window unbind or binding-lease expiry must not discard a Group that still owns participants.
  • A replacement generation supersedes only the matching (groupId, workspaceKey) binding; a late old-token disconnect cannot unbind its successor.
  • A copied Group/slot presented by a second live root forks rather than hijacking; a stale bookmarked popup URL fails closed.
  • The two-Workstation falsifier proves A+popup share one Group, B shares only appName, and reloading A touches neither B nor a second Group.
  • The first manager increment carries a measured implementation code-line addition/deletion ledger with a maximum +65 net implementation-line increase (explicit author amendment), replacing the original net-negative constraint for this ownership cut. Re-measure the final review head; this is not a general growth exemption. The ledger covers src/ + apps/ + examples/, using the repository's Acorn-based check-file-sizes.mjs counter. This population covers the engine and both named consumers, Workstation and DemoB; tests, generated assets and documentation do not pay for the increment. Report raw added/deleted lines separately, and identify each replaced parsing/admission/subscription/membership path with its single successor owner; formatting-only or unrelated deletions do not discharge the ownership cut. The shared TearOut identity contract, both consumers' admission/retirement callers, and their window carriers change together: both consumer popups join the opener's Group and stale-generation retirement remains refused. The #18317 subscriber deletion is included here; no compatibility branch, retained consumer alias or independent legacy membership registry remains.
  • ArchitectureOverview.md adds the missing src/manager/ inventory row and links ADR 0029.

Out of Scope

History rows/cursor, dock commit semantics, cross-Group bridging, or cold topology hydration.

Avoided Traps

No groupId = appName; no Group id in two carriers; no destroy-on-unbind; no realm-wide geometry disarm; no dock-aware manager.

Related

Part of #18303. Blocked by the hard-cut ADR/wire leaf. Enables #18284 and every Group consumer.

Origin Session ID: 01a06b6d-3323-7fa0-a10b-bbc4871c8780

Retrieval Hint: D18224 groupId workspaceKey generationToken warm reload carrier

Creation freshness: live latest-open and all-status A2A sweeps found no equivalent; #18284 consumes this identity but does not own it.