Problem scope
The dense workstation shipped (apps/workstation, the #15099→#15144 arc) and two-window transfer is real (#14772), but the release demo stops at the window edge. The wow bar — the reason this needs Neo rather than any web framework — is the five-beat filmable story ratified on D#15204:
- Tear-out: drag a tab past the window edge → it becomes a real OS popup window, left where dropped.
- Second tear-out: another tab converts to a popup while dragging.
- Popup-over-popup: drag it over the first popup → dock-zone previews light inside the other popup.
- Dock: release → both panes docked in one popup, still live (stores streaming, heartbeats never reset).
- Reintegration: drag the merged stack back into main → previews light, drop commits, the emptied popup closes itself.
Every beat on the shared App-Worker heap — the same JavaScript instances throughout. Portal-style DOM tricks (Vue/React) cannot do this: they re-render into a foreign document; Neo moves the living instance. This is the engine-category differentiator the release recording leads with.
Why an Epic: the work spans the drag stack (src/draggable), the window manager (Neo.manager.Window / DragCoordinator), the dock model + reconcilers (src/dashboard), an ADR amendment, a three-OS portability spike, and multiple app consumers — ≥6 coordinated one-PR leaves with hard merge-order constraints. No single ticket can carry it.
Intended solution shape
- The landed lineage is the grammar:
#7204 Dynamic Proxy Transitioning + #8160 direction-aware hysteresis (SortZone.checkWindowBoundary(), 0.8 detach / 0.6 reattach) + the landed live-popup embodiment (dashboard.Container#onDragBoundaryExit/Entry, DragDrop pointer-follow) are REUSED, never parallel-built. The dock lifts its enableProxyToPopup opt-out into dock semantics.
- Portability is measured, not assumed: a headed three-OS spike emits the seven-row
PASS_NATIVE | PASS_FALLBACK | FAIL matrix (hysteretic grammar · acquisition · moving embodiment · object permanence · screen topology · multi-window targeting · terminal cleanup); its RESULTS select implementation defaults. windowOpen returns a Boolean (a blocked popup never throws) — receipts assert it.
- Determinism is contract, not registration order: hover arbitration = a session-scoped gesture/claim protocol on stable workspace/zone identity (deterministic tie / stale / no-claim, fail closed; a ≥3-window overlap witness proves exactly one preview + one commit). The gesture-to-effect boundary is a finite outcome machine (
IN_SOURCE → DETACHED_MOVING → HOVERING_CLAIM → {COMMITTED_TARGET | TERMINAL_DETACHED | REJECTED | CANCELLED}) — source cleanup and vessel close only after COMMITTED_TARGET; commit precedes close; everything idempotent exact-once.
- Authority rides an ADR amendment:
Decision Record: REQUIRED — amends ADR 0029 (no new ADR, no supersede) in a separate merge-ordered PR owning arbitration, outcome states, and vessel lifecycle — landing BEFORE any consuming implementation.
- Ownership boundary (operator-ratified 2026-07-16): shared docking logic lives in
src/dashboard as app-neutral owners; apps/agentos (FM + future harness) and apps/workstation (the standalone flagship demo) are thin consumers; theming flows through the --agent-dock-preview-* domain aliases each app projects (PR #14955 / PR #15208 boundary; #15144's no-AgentOS/no-FM workstation contract preserved).
- Merge order (write-surface map): spike → settle the active G5/
#15207 surfaces → ADR amendment → G1 (dock adapter) → G3 (composition/arbitration) → G4 (stack/lifecycle/teardown) → the wow-demo leaf. Placement hints stay additive on dockLayout.v2.
Signal Ledger (§6.6 — graduation record, D#15204)
| Family |
Voice |
Signal |
Anchor |
| fable (author) |
Clio (@neo-fable-clio) |
[GRADUATION_PROPOSED] at cycle-4b |
body anchor 2026-07-16T09:12:06Z + comment DC_kwDODSospM4BDXXa |
| fable |
Mnemosyne (@neo-fable) |
[NO_OBJECTION] + evidence deposit |
DC_kwDODSospM4BDXXd |
| GPT (non-author) |
Emmy (@neo-gpt-emmy) |
[GRADUATION_APPROVED] — quorum met |
Step-Back DC_kwDODSospM4BDXN_ → narrow re-poll DC_kwDODSospM4BDXW_ → approval (cycle-4b) |
Quorum: ≥2 active families with signal ✓ · ≥1 non-author family approval ✓ (per §swarm_topology_anchor consensus-mandate).
Unresolved Liveness (carried from the Discussion): OQ4 — the design-language pick (operator side-by-side, post PR #15208; skin-tier only) · OQ5a — stack source projection vs transferNode() root rejection (G4's first design task; additive dockLayout.v2 only) · OQ7 — multi-screen assumptions (the spike's row-5 contract; getScreenDetails never a prerequisite).
revalidationTrigger: reopen D#15204 and pause consuming implementation if the spike matrix returns FAIL for any universal invariant (gesture continuity, same-instance permanence, JSON-only persisted state, exact-once commit, idempotent cleanup); if the ADR 0029 amendment cannot preserve the folded claim/outcome contracts; or if OQ5a requires non-additive dockLayout.v2 schema evolution.
Criteria mapping: D#15204 cycles 1–4c resolved OQ1/OQ2/OQ3/OQ5b/OQ6/OQ8 to [RESOLVED_TO_AC] contracts; those ACs land in the leaf tickets (per epic-create, this body carries none).
Out of scope
In-window docking polish (the shipped #14974/#14772 family) · the G5 design-language SELECTION itself (rides #15206 + OQ4, operator's eye) · FM cockpit surfaces (the #14610 successor CONSUMES the G1/G2 seam as first contracted consumer; it does not extend this epic) · Electron shell materialization (ADR 0034 may improve vessels, never fork placement/arbitration semantics) · amending #13158/#14789 (deliberately parented standalone; both stay Related).
Avoided traps (divergence preserved from D#15204)
- Release-time
window.open — WHATWG activation expires; retained only as the matrix's negative control.
windowId/registration-order target identity — the falsified Window.getWindowAt() first-intersecting behavior; the claim contract replaces it.
- A second drag system / dwell coordinator — the landed
#13028 450ms/250ms native-popup candidate path is extended, never duplicated.
- Verbatim cross-app porting — PR
#15237's lesson: precedent spanning app ownership + async lifecycle lifts into the shared owner (src/dashboard), never copies across an app boundary.
- Silent re-scoping of adjacent epics —
#13158 (in-window) and #14789 (four-beat fusion tour) keep their own scopes.
Related
Source: D#15204 (graduated; anchors above) · ADR 0029 (amended by a leaf) · ADR 0020/0034 · #13158 · #14789 · #15144 · PR #14772 / PR #15193 / #13028 / #8160 / #7204 (the lineage) · #15206 / #15207 (re-parented leaves).
Origin Session ID: c5d7cd6b-4e01-45fd-aa59-5ccbc0e5f091
Retrieval Hint: "multi-window docking choreography epic tear-out popup claim contract outcome machine ADR 0029 amendment"
Problem scope
The dense workstation shipped (apps/workstation, the
#15099→#15144arc) and two-window transfer is real (#14772), but the release demo stops at the window edge. The wow bar — the reason this needs Neo rather than any web framework — is the five-beat filmable story ratified on D#15204:Every beat on the shared App-Worker heap — the same JavaScript instances throughout. Portal-style DOM tricks (Vue/React) cannot do this: they re-render into a foreign document; Neo moves the living instance. This is the engine-category differentiator the release recording leads with.
Why an Epic: the work spans the drag stack (
src/draggable), the window manager (Neo.manager.Window/DragCoordinator), the dock model + reconcilers (src/dashboard), an ADR amendment, a three-OS portability spike, and multiple app consumers — ≥6 coordinated one-PR leaves with hard merge-order constraints. No single ticket can carry it.Intended solution shape
#7204Dynamic Proxy Transitioning +#8160direction-aware hysteresis (SortZone.checkWindowBoundary(), 0.8 detach / 0.6 reattach) + the landed live-popup embodiment (dashboard.Container#onDragBoundaryExit/Entry,DragDroppointer-follow) are REUSED, never parallel-built. The dock lifts itsenableProxyToPopupopt-out into dock semantics.PASS_NATIVE | PASS_FALLBACK | FAILmatrix (hysteretic grammar · acquisition · moving embodiment · object permanence · screen topology · multi-window targeting · terminal cleanup); its RESULTS select implementation defaults.windowOpenreturns a Boolean (a blocked popup never throws) — receipts assert it.IN_SOURCE → DETACHED_MOVING → HOVERING_CLAIM → {COMMITTED_TARGET | TERMINAL_DETACHED | REJECTED | CANCELLED}) — source cleanup and vessel close only afterCOMMITTED_TARGET; commit precedes close; everything idempotent exact-once.Decision Record: REQUIRED — amends ADR 0029(no new ADR, no supersede) in a separate merge-ordered PR owning arbitration, outcome states, and vessel lifecycle — landing BEFORE any consuming implementation.src/dashboardas app-neutral owners;apps/agentos(FM + future harness) andapps/workstation(the standalone flagship demo) are thin consumers; theming flows through the--agent-dock-preview-*domain aliases each app projects (PR#14955/ PR#15208boundary;#15144's no-AgentOS/no-FM workstation contract preserved).#15207surfaces → ADR amendment → G1 (dock adapter) → G3 (composition/arbitration) → G4 (stack/lifecycle/teardown) → the wow-demo leaf. Placement hints stay additive ondockLayout.v2.Signal Ledger (§6.6 — graduation record, D#15204)
[GRADUATION_PROPOSED]at cycle-4b2026-07-16T09:12:06Z+ commentDC_kwDODSospM4BDXXa[NO_OBJECTION]+ evidence depositDC_kwDODSospM4BDXXd[GRADUATION_APPROVED]— quorum metDC_kwDODSospM4BDXN_→ narrow re-pollDC_kwDODSospM4BDXW_→ approval (cycle-4b)Quorum: ≥2 active families with signal ✓ · ≥1 non-author family approval ✓ (per
§swarm_topology_anchorconsensus-mandate).Unresolved Liveness (carried from the Discussion): OQ4 — the design-language pick (operator side-by-side, post PR
#15208; skin-tier only) · OQ5a — stack source projection vstransferNode()root rejection (G4's first design task; additivedockLayout.v2only) · OQ7 — multi-screen assumptions (the spike's row-5 contract;getScreenDetailsnever a prerequisite).revalidationTrigger: reopen D#15204 and pause consuming implementation if the spike matrix returns
FAILfor any universal invariant (gesture continuity, same-instance permanence, JSON-only persisted state, exact-once commit, idempotent cleanup); if the ADR 0029 amendment cannot preserve the folded claim/outcome contracts; or if OQ5a requires non-additivedockLayout.v2schema evolution.Criteria mapping: D#15204 cycles 1–4c resolved OQ1/OQ2/OQ3/OQ5b/OQ6/OQ8 to
[RESOLVED_TO_AC]contracts; those ACs land in the leaf tickets (per epic-create, this body carries none).Out of scope
In-window docking polish (the shipped
#14974/#14772family) · the G5 design-language SELECTION itself (rides#15206+ OQ4, operator's eye) · FM cockpit surfaces (the#14610successor CONSUMES the G1/G2 seam as first contracted consumer; it does not extend this epic) · Electron shell materialization (ADR 0034 may improve vessels, never fork placement/arbitration semantics) · amending#13158/#14789(deliberately parented standalone; both stayRelated).Avoided traps (divergence preserved from D#15204)
window.open— WHATWG activation expires; retained only as the matrix's negative control.windowId/registration-order target identity — the falsifiedWindow.getWindowAt()first-intersecting behavior; the claim contract replaces it.#13028450ms/250ms native-popup candidate path is extended, never duplicated.#15237's lesson: precedent spanning app ownership + async lifecycle lifts into the shared owner (src/dashboard), never copies across an app boundary.#13158(in-window) and#14789(four-beat fusion tour) keep their own scopes.Related
Source: D#15204 (graduated; anchors above) · ADR 0029 (amended by a leaf) · ADR 0020/0034 ·
#13158·#14789·#15144· PR#14772/ PR#15193/#13028/#8160/#7204(the lineage) ·#15206/#15207(re-parented leaves).Origin Session ID: c5d7cd6b-4e01-45fd-aa59-5ccbc0e5f091 Retrieval Hint: "multi-window docking choreography epic tear-out popup claim contract outcome machine ADR 0029 amendment"