From 7abf097b10b4c71bc98c8bc4abb72e2bbe4a5ae3 Mon Sep 17 00:00:00 2001 From: Danyang Chen Date: Wed, 28 Oct 2026 08:06:02 +0800 Subject: [PATCH] docs(core-client): the Workflows flowchart is read-only until Edit (#1378) Replay of 46941fc3 onto main fe6c9069. The same three doc claims as the reviewed tip, re-applied on top of the text main gained since 26657c8a: core-client.md's Workflows bullet now also carries #1434's rename/delete sentence, so the read-only wording replaces the drag wording inside it. Docs only, 2 files, +3/-3 lines. --- docs/subsystems/core-client.md | 2 +- docs/subsystems/workflow.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/subsystems/core-client.md b/docs/subsystems/core-client.md index f692f8b63..a3cd1b0f8 100644 --- a/docs/subsystems/core-client.md +++ b/docs/subsystems/core-client.md @@ -182,7 +182,7 @@ The overlay (`flybot-slots.js` `FlyBotApprovals`) is the fallback, not a second - The + menu is a popover anchored next to the sidebar **+** (#195, #173): its `.bt-pane` carries `data-menu` and stays see-through, the menu is a 280px card at the top left with no title row, and a press on the pane outside the card (or Escape) closes it. New chat, New group chat, New Bot and plugin rows open their full pane as before. - Settings → **Workflows** flowchart wire plane (#1459): `GraphCanvas` in `workflows.js` places nodes on a zero-width center line (`.bt-wf-plane`). Connector paths are drawn in an SVG shifted by `WIRE_SPAN` (480): the plane sets `--bt-wf-wire-span` on its inline style and `.bt-wf-wires` in `styles.js` uses `left:calc(-1 * var(--bt-wf-wire-span))` and `width:calc(2 * var(--bt-wf-wire-span))`, so coordinates `node.x + WIRE_SPAN` line up with each card's center instead of fanning to a corner. The `@flybot/workflow` stage drawer uses the same anchoring (`--wf-wire-span` on `.wf-plane`, matching rules on `.wf-wires`). `NODE_WIRE_OUT` is the bottom port offset for the cubic segment. Tests: `plugins/workflow/test/client.test.js` (`wirePath` regression); Settings: `core/test/workflows-page.test.js`. -- Settings → **Workflows** (`src/client/workflows.js`, #1364) sits right after **Scheduled tasks**, beside the other automation page, so the page list's existing order and the plugin-slot rule (a `flybot.settings.page` entry is listed after Connectors) are unchanged. It is not a second row in the account menu: the menu keeps its one name per destination. The page reads `@flybot/workflow` through `call('workflow/state')`: **Factory presets** are the template catalog (a drawable graph, so a pipeline can be opened before it is copied) and **Yours** are the workflows saved on this machine whose ids are not those templates. Each user-owned workflow row shows rename (✎) and delete (×) action buttons; factory presets show no actions and refuse both operations (#1434). Rename opens a modal with a name field and calls `workflow/rename`; delete confirms ("Are you sure you want to delete '{name}'? This cannot be undone.") and calls `workflow/delete`, which removes the workflow from state, releases ephemeral Bots if a run exists, and cleans logs/runs/routes. Opening one draws the flowchart; a saved workflow can be dragged (move a node, draw an edge, drop a local Bot, remove a node) and each node's **system prompt** can be typed in a field beside the canvas — that prompt is what the node's run Bot receives — and the picture is saved with `workflow/graph` (#1393). **Use this** copies a factory template with `workflow/create`. A read that rejects or never returns settles as an error with **Retry**; an empty store says there are no workflows. When that read is `bot/not-found`, or the host says `Unknown FlyBot endpoint workflow/…`, the page says the Workflow plugin is not installed or not enabled (a default install includes `@flybot/workflow`) in the interface language, and **Retry** stays (#1381). Any other failure still shows its own reason. The page does not teach `workflows/.json`. The definition engine that still stores those files stays on the host and is not this page. Tests: `core/test/workflows-page.test.js`. +- Settings → **Workflows** (`src/client/workflows.js`, #1364) sits right after **Scheduled tasks**, beside the other automation page, so the page list's existing order and the plugin-slot rule (a `flybot.settings.page` entry is listed after Connectors) are unchanged. It is not a second row in the account menu: the menu keeps its one name per destination. The page reads `@flybot/workflow` through `call('workflow/state')`: **Factory presets** are the template catalog (a drawable graph, so a pipeline can be opened before it is copied) and **Yours** are the workflows saved on this machine whose ids are not those templates. Each user-owned workflow row shows rename (✎) and delete (×) action buttons; factory presets show no actions and refuse both operations (#1434). Rename opens a modal with a name field and calls `workflow/rename`; delete confirms ("Are you sure you want to delete '{name}'? This cannot be undone.") and calls `workflow/delete`, which removes the workflow from state, releases ephemeral Bots if a run exists, and cleans logs/runs/routes. Opening one draws the flowchart read-only: a saved workflow gets **Edit** (`WorkflowsView` in `src/client/workflows.js`), and only then can a node be moved, an edge drawn, a local Bot dropped, a node removed, and each node's **system prompt** typed in a field beside the canvas — that prompt is what the node's run Bot receives — and the picture is saved with `workflow/graph`; **Finish editing** leaves that mode (#1393, #1378). A factory preset stays read-only until **Use this** copies it with `workflow/create`, and that copy opens in Edit. A read that rejects or never returns settles as an error with **Retry**; an empty store says there are no workflows. When that read is `bot/not-found`, or the host says `Unknown FlyBot endpoint workflow/…`, the page says the Workflow plugin is not installed or not enabled (a default install includes `@flybot/workflow`) in the interface language, and **Retry** stays (#1381). Any other failure still shows its own reason. The page does not teach `workflows/.json`. The definition engine that still stores those files stays on the host and is not this page. Tests: `core/test/workflows-page.test.js`. - The sidebar footer button (`ConnectApps` in `sidebar.js`, label **Connect apps** / 连接应用) opens Settings > Connectors through `actions.openConnectors()`, the same call and the same words as the account menu's **Connect apps** row. The shell's plugin panel stays reachable in the Bot surface through the account menu's **Plugins** row (`actions.selectPanel('plugins')`, 插件), without switching to classic Agent mode. - One name per destination in the account menu and command palette (#392, #393, #394): **Settings** opens the Settings dialog (General), since **Bot settings** / **Edit Bot** open one Bot; **Scheduled tasks** opens Settings → Scheduled tasks, the name Settings and a Bot's details use (DSH's own bundle stays "Automation tasks" in the Plugins hints); and **Switch to classic Agent mode** is the only way to classic Agent mode, so Settings → General is Appearance only. - Create Bot starts on the Main Bot's model (#230): `startModel()` (`bot-draft.js`) picks the Main Bot's chosen model, else the one it last ran on, when it is served; then `roster.defaultModel`, then the first served model. Only served models are picked, so a provider without a credential is never preselected. diff --git a/docs/subsystems/workflow.md b/docs/subsystems/workflow.md index d027caadb..2959f6594 100644 --- a/docs/subsystems/workflow.md +++ b/docs/subsystems/workflow.md @@ -6,7 +6,7 @@ owned_paths: # workflow (`@flybot/workflow`) -A group-shaped chat of local Bots, with a flowchart the owner can drag (#1318). The plugin stores the graphs under `$DSH_HOME/bot/flybot-workflow` and answers `flybot/workflow/*`. It lives entirely under `plugins/workflow/`. +A group-shaped chat of local Bots, with a flowchart the owner can drag once **Edit** is on (#1318, #1378). The plugin stores the graphs under `$DSH_HOME/bot/flybot-workflow` and answers `flybot/workflow/*`. It lives entirely under `plugins/workflow/`. This is not the workflow-definition engine. That engine (#1272) stores `workflows/.json` and guards artifacts. This plugin does not read or write those files and does not call that engine. @@ -45,7 +45,7 @@ Everything in `plugins/workflow/`: the graph rules, the run store, the matcher, ## Invariants 1. Friends' Bots are not nodes. A `remote` Bot is refused by create, define and the matcher. -2. The canvas and the run share one graph: nodes, edges and positions. A drag commits `workflow/graph`. Status highlighting reads that same graph. Replacing edges does not recompute who is already running. +2. The canvas and the run share one graph: nodes, edges and positions. A drag commits `workflow/graph`, but only while **Edit** is on — a factory preset is copied first (#1378). Status highlighting reads that same graph. Replacing edges does not recompute who is already running. 3. **A live run advances only when its ephemeral Bot replies, following the graph's edges in order.** That reply advances the run and may appear on the workflow stage log; it is not delivered into a persistent sidebar Bot conversation (for example Chief). The `workflow-step` send middleware denies those replies after `stepResult` runs. Nodes start only when their forward predecessors finish and edges reach them. There is no scripted or seeded run path: `workflow/state` exposes only `live === true` runs. A run does not create a schedule. `scheduleLabel` is display text. When the host can create Bots, starting a live run (`assign`, `run_workflow`, `ingest`) spawns one ephemeral Bot per node from that node's prompt, role, model and tools, sends it the step from the Main Bot, and removes every instance when the run finishes, fails for good, or is reset. A `failOnce` retry keeps them. They are hidden and left out of the sidebar. Loading the plugin does not spawn Bots for the default workflows. Plugin unload (`dispose`) and the load-time `reap` both end a live run still marked `running`: in-flight nodes are failed, the run is archived as `failed`, and any ephemeral Bot ids are released. A run already written as `running` with no ephemeral map (an older unload) is healed the same way on the next load (#1414). Loading is also the one moment a run the host left behind is noticed when its ephemeral map is still present: `reap` releases those instances and records the run as `failed`. Nothing resumes it. A run that already ended keeps its own status (#1388). A dispatch that does not land is not marked handed. The chat names the code, and the next hand-off tries that node again. It is not sent twice in the same hand-off. A reply that says `could not answer your message` or `model call failed` fails that node and does not start its successor. A 429 or rate-limit mention in any other reply does not. 4. Each workflow keeps a FIFO queue and up to `maxConcurrent` live runs (default 2, an integer from 1 to 8 on the record or via `workflow/concurrency`). The host runs at most 6 live runs at once. A task that does not fit is enqueued. When a run finishes, every workflow with a free slot is drained, oldest enqueue first, until the host cap. A new assign on a workflow that already has a queue waits behind it. A task does not replace a running run or release that run's Bots. Migrating an old store keeps only runs that were still `running` with ephemeral Bot ids; other legacy scripted rows are dropped from active state (finished tasks may remain in lane history). History records a finished or failed live run. Lowering the cap does not stop a run that has already started. Assigning `{ botId }` claims the card for that local Bot and does not enqueue. A remote Bot is refused. `activeCount` is `runningOf(lane)` — live runs with `live === true` and `status === 'running'`. The stage aggregate chip and the Runs tab (运行记录) must agree: every such run appears as its own running row and progress card, and the chip count does not exceed that list. 5. A node declares up to eight inputs and eight outputs. A required input is an output of the same name on an ancestor, or `task`, `title`, or `brief` on a root. A start with no provider is refused, and Bots spawned for that attempt are released. On a Bot-backed run a successor stays queued until its required inputs are present. If every forward predecessor is done, failed or skipped and an input is still missing, that node fails and the run stops. The dispatch text carries `Input : `. The Bot reports `OUTPUT : `; a single declared output may be the reply body. Nodes start in edge order.