Files
tutti/docs/architecture/workbench-node-lifecycle.md
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

363 lines
11 KiB
Markdown

# 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-files`
- `browser`
- `terminal`
- `agent`
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](./workbench-dock-model.md).
### 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:
```ts
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:
```ts
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:
```text
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:
```ts
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`, and
`instanceKey`
- 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:
```text
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 compatibility
- `packages/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:
- `projectedNodes` for host-owned presence
- `onLaunchRequest` and `launchNode(...)` for async dock, shortcut, command, or
host opens
- `onNodeCloseRequest` and `requestNodeClose(...)` for explicit host close
policy
- `activateNode(...)` and render-context `activation` for transient one-shot
actions
- `externalStateSource` for host-owned business view state
- `createWorkbenchHostProjectedNodeId(...)` and
`createWorkbenchHostLaunchedNodeId(...)` 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