11 KiB
Workbench Node Lifecycle
This document describes the lifecycle model for shared Workbench nodes.
The goal is to keep @tutti-os/workbench-surface usable by Tutti desktop and
TSH without turning Workbench into a second business core.
Ownership Model
Workbench owns node shell presentation:
- node shell creation, removal, focus, drag, resize, snap, and minimize mechanics
- frame, display mode, restore frame, minimized state, and stack order
- generic dock and window chrome mechanics
- snapshot persistence for shell presentation state
Host code owns business state:
- whether a business instance exists
- how a business instance is created, reused, closed, or restored
- node body data such as terminal cwd, exit code, browser runtime URL, agent session status, workspace tree state, or task progress
- business persistence and external runtime subscriptions
Workbench may receive stable identity metadata for a node shell, but it must not become the durable store for business payloads.
Core Concepts
The Workbench host interface should separate five concepts that were previously easy to blur together:
- node definition
- projected node presence
- launch request
- activation
- snapshot contract
Node Definition
A node definition describes how a node type renders and behaves at the shell level.
It answers: "Given a node shell of this type, how should Workbench render it?"
Examples:
workspace-filesbrowserterminalagent
Node definitions may include:
typeId- default title and frame
- body and header renderers
- shell capabilities such as closable, minimizable, or fullscreenable
Node definitions must not own host business state or host-specific transport.
Dock presentation and grouping should be modeled separately at the host level. See Workbench Dock Model.
Projected Node Presence
Projected node presence is a declarative input from the host into Workbench.
It answers: "Which host-owned business instances should currently have a Workbench shell?"
This is for nodes whose presence is owned by host or runtime facts, such as:
- terminal sessions that already exist in a runtime
- agent sessions created by a host service
- other host-owned long-lived sessions whose UI should be projected into the Workbench
Illustrative shape:
export interface WorkbenchProjectedNode {
typeId: string;
instanceId: string;
instanceKey?: string | null;
title: string;
defaultFrame?: WorkbenchFrame;
subject?: {
type: string;
id: string;
};
}
Semantics:
- when the host projects a node, Workbench reconciles a shell into current Workbench state
- when a projected node has the same stable shell identity as a snapshot node, Workbench restores presentation state from the snapshot
- when a projected node is no longer present in the host input, Workbench removes the shell by default
- projected node disappearance is not a business close policy; hosts that want an ended session to remain visible should keep projecting it and expose the ended state through host-owned external state
Projected presence should be reconciled in the Workbench host/session layer, not in the lower-level reducer. The reducer should remain a pure shell state machine for window mechanics.
Projected node subject is live lookup metadata. It should not be persisted in
the Workbench snapshot unless it later becomes a deliberately minimal shell
identity field. The default position is to keep it out of snapshots.
Launch Request
A launch request is a command from Workbench or host UI asking the host to create or resolve a business instance.
It answers: "The user or shell wants to open this kind of node; host, which business instance should back it?"
This keeps "create or reuse an instance" as an explicit host decision instead of folding it into a catch-all shell command.
Illustrative shape:
export interface WorkbenchLaunchRequest {
typeId: string;
reason: "dock" | "command" | "shortcut" | "host";
}
export interface WorkbenchLaunchResult {
typeId: string;
instanceId: string;
instanceKey?: string | null;
title?: string;
defaultFrame?: WorkbenchFrame;
activation?: WorkbenchActivation;
}
Launch requests may be asynchronous.
This supports flows such as:
User clicks Terminal in the dock
-> Workbench sends a launch request
-> host creates or reuses a terminal session
-> host returns stable shell identity
-> Workbench opens or focuses the shell
Workbench should not infer business creation policy. The host decides whether a launch creates a new instance, reuses an existing instance, fails, or maps to an already projected instance.
Activation
Activation is a one-shot business action delivered to an existing node shell.
It answers: "This node already exists; ask it to do this one thing."
Examples:
- reveal a path in the files node
- open a URL in a browser node
- focus a pane inside a terminal node
- jump to a message in an agent node
Illustrative shape:
export interface WorkbenchActivation {
type: string;
payload?: unknown;
sequence: number;
}
Activation does not decide whether a node exists. If an action requires a node to exist first, the caller should use launch or projected presence to obtain a shell, then deliver activation.
Activation targets may be addressed by concrete shell node id, or by
typeId plus instanceId. Single-instance nodes may also be addressed by
typeId alone. Multi-instance nodes require an explicit shell id or instance
id to avoid ambiguous delivery.
Activation payloads are transient and must not be written to the Workbench snapshot.
Snapshot Rules
Workbench snapshots store shell presentation state only.
Snapshot purity is a P0 architecture requirement. It should be enforced by a package-level sanitizer or validator before host snapshots are persisted.
Allowed snapshot data:
- node shell id
- node kind or type id
- title
- frame
- display mode
- restore frame
- minimized state
- node stack
- minimal shell identity metadata such as
typeId,instanceId, andinstanceKey - projected shell marker such as
isProjected
Disallowed snapshot data:
- terminal cwd
- terminal process status or exit code
- browser runtime URL or page state unless deliberately modeled as shell identity
- agent session status, messages, or active task state
- workspace tree contents
- launch payloads
- activation payloads
- host-owned subject details
Reload behavior for host-owned projected nodes:
snapshot contains layout for terminal:session-1
host projects terminal session-1
-> Workbench restores shell layout
snapshot contains layout for terminal:session-1
host does not project terminal session-1
-> Workbench does not restore the shell
The snapshot may remember presentation for a shell identity, but it is not the source of truth for host-owned presence.
Snapshot sanitation should preserve only the shell fields Workbench owns and the minimal identity metadata required to reconnect shell layout with a host-owned business instance. Host-owned business payloads should be stripped or rejected before persistence.
External State
Node renderers should read host-owned business state through an external state source or equivalent host adapter.
The lookup input should include stable shell identity:
- workspace id
- node id
- type id
- instance id
- instance key when available
If projected node subject is needed for lookup, it may be included in live
render context or external state lookup input. That does not imply that
subject belongs in the persisted snapshot.
Close Semantics
Workbench close removes or requests removal of a shell. It must not implicitly decide host business lifecycle.
For host-owned projected nodes, close usually needs a host callback such as
onNodeCloseRequest. The host decides whether close means:
- hide the shell while keeping the business instance alive
- terminate the business instance
- mark the instance closed and stop projecting it
- reject or ignore the close
If the host continues projecting the same node after a shell close, Workbench will reconcile it back into view. Host-owned close policy must therefore be explicit.
Placement In The Package
Projected presence reconciliation belongs in packages/workbench/surface at the
Workbench host/session layer.
It should not be pushed into:
packages/workbench/snapshot, which owns durable snapshot compatibilitypackages/workbench/service, which owns Go snapshot validation and storage- the core reducer, which should stay focused on shell state transitions
- product-specific desktop feature code, where reconciliation would become duplicated host glue
The lower-level reducer should remain deep and small: given a shell state and a window-mechanics action, return the next shell state.
The host/session layer is the right module for combining:
- node definitions
- current projected presence
- loaded snapshot
- launch results
- Workbench controller commands
Current Package Contract
@tutti-os/workbench-surface exposes the Workbench host lifecycle through
these public mechanisms:
projectedNodesfor host-owned presenceonLaunchRequestandlaunchNode(...)for async dock, shortcut, command, or host opensonNodeCloseRequestandrequestNodeClose(...)for explicit host close policyactivateNode(...)and render-contextactivationfor transient one-shot actionsexternalStateSourcefor host-owned business view statecreateWorkbenchHostProjectedNodeId(...)andcreateWorkbenchHostLaunchedNodeId(...)for stable shell id derivation
Snapshot sanitization is package-internal. Hosts should provide a repository; Workbench owns stripping business payloads before persistence.
The public node definition contract describes shell and rendering behavior:
- type id
- default frame
- dock presentation
- chrome behavior
- render body and header
- shell capabilities
- single or multi shell mode
It does not describe business instance creation, reuse, close policy, or business state persistence. Those stay in the host adapter or host services.
Extension Guardrails
Allowed helper direction:
- stable shell identity helpers
- snapshot sanitation helpers
- small type guards for projected node inputs
Avoid helpers that make hosts manually restore frame or stack state from snapshots. Layout restoration should remain behind WorkbenchHost reconciliation.
Avoid definition fields that encode business creation or reuse policy. When a
host needs custom creation behavior, use onLaunchRequest.
Acceptance Criteria
- projected node first appearance creates a visible shell
- projected node with matching snapshot identity restores frame, display mode, minimized state, and stack order
- projected node disappearance removes the shell by default
- ordinary dock and launch-created nodes continue to work
- user layout changes are persisted as shell presentation state
- snapshot does not persist business payloads or activation payloads
- node renderers can read host-owned data through external state lookup
- close behavior for host-owned projected nodes is explicit and testable