Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1443-workflow-demo-ui.removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Workflow stage no longer offers Replay demo, Run demo, or stepped canned playback ([#1443](https://github.com/ThinkFlowLab/FlyBot/issues/1443)). Assign and live runs still spawn ephemeral Bots per node; `workflow/demo-step` remains for tests only.
6 changes: 3 additions & 3 deletions docs/subsystems/workflow.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
subsystem: workflow
owned_paths:
Expand Down Expand Up @@ -33,7 +33,7 @@
| `plugins/workflow/src/match.js` | Which open card matches a Workflow or a local Bot. |
| `plugins/workflow/src/template.js` | 发布交付流水线 (a parallel branch) and the weekly report, each node with the inputs and outputs that edge carries. |
| `plugins/workflow/src/narrate.js` | Progress lines for a step. A live run quotes the Bot reply. The canned delivery copy is the demo player only. A join line is only for a rank of more than one node. |
| `plugins/workflow/client.js` | Sidebar section, chat stage, assign dialog, flowchart editor, and Settings → Workflow graphs. Replay demo is disabled while a live run exists. The stage shows the queue and refreshes state while one is active. The stage and the dialogs portal onto `document.body`; a missing container stays in the section tree. |
| `plugins/workflow/client.js` | Sidebar section, chat stage, assign dialog, flowchart editor, and Settings → Workflow graphs. The product UI starts only live runs (assign, ingest, composer); it does not call `workflow/demo-step`. The stage shows the queue and refreshes state while one is active. The stage and the dialogs portal onto `document.body`; a missing container stays in the section tree. |

## Entry points

Expand All @@ -46,7 +46,7 @@

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.
3. A live run advances only when its ephemeral Bot replies, and that reply is the chat line. `workflow/demo-step` replays the seeded script and refuses while that workflow has a live run; the stage does not start the replay timer while `activeCount` is greater than 0. 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 seeded fixture. Loading is also the one moment a run the host left behind is noticed: a run still marked `running` is abandoned, so loading releases its 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.
3. A live run advances only when its ephemeral Bot replies, and that reply is the chat line. `workflow/demo-step` exists for unit tests and scripted fixtures only; the browser client does not call it. 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 seeded fixture. Loading is also the one moment a run the host left behind is noticed: a run still marked `running` is abandoned, so loading releases its 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. An old store run is a demo unless it is still `running` and has ephemeral Bot ids. History records a finished or failed run only. 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.
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 <name>: <value>`. The Bot reports `OUTPUT <name>: <value>`; a single declared output may be the reply body. Nodes start in edge order.
6. Each node is a real ephemeral Bot for that run, then it is removed. A permanent sidebar Bot (`botId`) is optional and is not required for the step to run. `tools` on the node is what that Bot may use: `github` grants `mcp__github__*` (pull requests, reviews, comments); `bash` or `shell` sets the permission to `danger-full-access` and allows a full-access shell; any other step gets `workspace-write`. A hidden run Bot cannot show an approval card, so a granted tool is allowed and a connector it did not name is refused. The host stores this on the run Bot as `stepTools` and `permission`.
Expand All @@ -55,7 +55,7 @@
9. The browser draws text only. Names and messages are React text children. The unit renderer asserts a hostile name is not markup.
10. `enabled: false` registers no endpoint, tool or listener and writes nothing.
11. A sidebar click opens the stage or a dialog and leaves the Workflows section in place. The stage and the dialogs portal onto `document.body`. When that container is missing they stay in the section tree; the click does not throw.
12. A node's prompt and the graph are one record. Settings → Workflow graphs, the flowchart's prompt fields, and `define_workflow` all write `workflow/graph`. A run reads that record; it does not define it. Replay steps the seeded script. After a live run has finished, the stage keeps that run until Replay steps the script. From that step on, the stage shows the demo, including its `nodeStatus`, ahead of the finished `lane.last`. A running live lane still wins. A migrated finished script is the same: it stays done until Replay, and then the presented nodes follow the demo.
12. A node's prompt and the graph are one record. Settings → Workflow graphs, the flowchart's prompt fields, and `define_workflow` all write `workflow/graph`. A run reads that record; it does not define it. After a live run finishes, the stage shows `lane.last` for that workflow until a new live run starts. Seeded fixture scripts stay in `state.demos` for tests; the product UI does not step them.

## Tests

Expand Down
12 changes: 4 additions & 8 deletions plugins/workflow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ This is not the workflow-definition engine (`workflows/<id>.json`, #1272). This

Assigning a task, `run_workflow`, or a Workflow match starts a live run. One hidden ephemeral Bot is created per node, used for that step, and removed when the run finishes. A node does not need a permanent sidebar Bot. `tools` on the node is what that Bot can use: `github` grants the GitHub connector (pull requests, reviews, comments), `bash` grants a shell at full access, and every step gets at least workspace write. The Main Bot sends that node its step. The node stays `running` until that Bot replies. The reply is the chat line for the step. The next node starts only after that reply, and only along the edges: every predecessor is done first, and nodes that share a rank run together.

A second task does not replace a run that is already going, and it does not release that run's Bots. It waits in a FIFO queue. The stage shows a chip when more than one run is active or the queue is not empty, and it refreshes `workflow/state` while a live run exists. That refresh does not advance the graph.

**Replay demo** plays the seeded script. It is disabled while a live run of that workflow is going, and `workflow/demo-step` refuses with "A live run is in progress". The demo's canned lines are not written for a live run.
A second task does not replace a run that is already going, and it does not release that run's Bots. It waits in a FIFO queue. The stage shows a chip when more than one run is active or the queue is not empty, and it refreshes `workflow/state` while a live run exists. That refresh does not advance the graph. The product UI does not offer a canned demo replay; only live runs advance the graph.

Each workflow allows `maxConcurrent` live runs at once. The default is 2. The value is an integer from 1 to 8, set on create (`maxConcurrent`) or with `workflow/concurrency` (`{ workflowId, maxConcurrent }`). The stage lists the current value under Details. The host runs at most 6 live runs across every workflow. A task that does not fit is queued. When any run finishes, the oldest queued task whose workflow still has room starts, whichever workflow it belongs to. A new assign waits behind a task already queued there. Lowering the cap does not stop a run that has already started. History keeps finished and failed runs only.

Expand All @@ -42,7 +40,7 @@ The delivery template passes `task` → `plan` → `copy` → `patch`, then `rev

An edge may be a condition or a loop. A condition is `when: { output, eq }` on the edge out of the node that produced `output`. The edge is taken only when that output equals `eq`. `when: { else: true }` is the other branch, taken when no `eq` edge matched. A node that no taken edge reaches is skipped. A later join starts once every incoming edge is either taken and done, or skipped.

A loop is `loop: true` on the edge that returns to the body, with `max` from 1 to 8 (default 3) and an optional `until: { output, eq }`. The body runs again while `until` does not match and the run count is still under `max`. A branch the previous pass skipped can be chosen the next time. The forward edges wait until the loop stops. A loop is the only cycle the graph allows. Settings → Workflow graphs can add a link, a condition, or a loop. Replay keeps showing a finished live run until you step the demo script; the next replay step is the script, and the graph's node status moves with it.
A loop is `loop: true` on the edge that returns to the body, with `max` from 1 to 8 (default 3) and an optional `until: { output, eq }`. The body runs again while `until` does not match and the run count is still under `max`. A branch the previous pass skipped can be chosen the next time. The forward edges wait until the loop stops. A loop is the only cycle the graph allows. Settings → Workflow graphs can add a link, a condition, or a loop. After a live run finishes, the stage keeps showing that run until a new one starts.

## When a step does not land

Expand Down Expand Up @@ -83,12 +81,10 @@ Workflow 是一组本地 Bot 的群聊式运行。侧栏打开同一次运行:

一次真实运行会为每个节点创建一个隐藏的临时 Bot,用完即删。节点不必绑定侧栏里的永久 Bot。`tools` 决定这个 Bot 能用什么:`github` 授予 GitHub 连接器(拉取请求、评审、评论),`bash` 授予完整权限的 shell,其余步骤至少可以写工作区。节点一直处于运行中,直到该 Bot 回复。回复写入 Workflow 聊天,下一节点才沿边开始。正在进行的运行不会被新任务替换,也不会因此释放它的 Bot。历史里只留下已完成或已失败的运行。

每个 Workflow 同时运行的上限是 `maxConcurrent`,默认 2,取 1 到 8 的整数,在创建时传入,或调用 `workflow/concurrency`。全部 Workflow 加在一起最多 6 个正在运行的任务。某个运行结束后,所有 Workflow 里最早排队、且自己还有空位的任务会开始。新的分配排在已经排队的任务后面。降低上限不会停掉已经开始的运行。舞台在详情里显示当前上限;有排队或同时多于一个运行时显示队列条。有真实运行时,页面只刷新状态,不播放演示。

**重放演示** 只播放预置脚本。该 Workflow 有真实运行时按钮不可用,`workflow/demo-step` 会拒绝。
每个 Workflow 同时运行的上限是 `maxConcurrent`,默认 2,取 1 到 8 的整数,在创建时传入,或调用 `workflow/concurrency`。全部 Workflow 加在一起最多 6 个正在运行的任务。某个运行结束后,所有 Workflow 里最早排队、且自己还有空位的任务会开始。新的分配排在已经排队的任务后面。降低上限不会停掉已经开始的运行。舞台在详情里显示当前上限;有排队或同时多于一个运行时显示队列条。有真实运行时,页面只刷新状态。产品界面不提供预置演示重放,只有真实运行会推进图。

每个节点最多声明 8 个输入和 8 个输出,名字为小写字母开头的短名。必填输入必须由上游同名输出提供;根节点可以用 `task`、`title` 或 `brief`。没有提供者时,运行拒绝开始。Bot 用 `OUTPUT 名字: 值` 交回输出;只声明一个输出时,回复正文即可。

边可以是条件或循环。条件是 `when: { output, eq }`,只有该输出等于 `eq` 才走这条边;`when: { else: true }` 是其余分支。没被选中的节点标为跳过,汇合节点不因此卡住。循环边是 `loop: true`,`max` 为 1 到 8(默认 3),可选 `until: { output, eq }`。在达到 `max` 或 `until` 命中之前,循环体再跑一遍,正向的边先等着。上一轮跳过的分支,下一轮可以再选。只有循环边可以成环。设置页可以添加连线、条件或循环。真实运行结束后,舞台仍显示那次运行,直到重放演示再走一步,之后显示的是演示脚本,节点状态跟着演示走。
边可以是条件或循环。条件是 `when: { output, eq }`,只有该输出等于 `eq` 才走这条边;`when: { else: true }` 是其余分支。没被选中的节点标为跳过,汇合节点不因此卡住。循环边是 `loop: true`,`max` 为 1 到 8(默认 3),可选 `until: { output, eq }`。在达到 `max` 或 `until` 命中之前,循环体再跑一遍,正向的边先等着。上一轮跳过的分支,下一轮可以再选。只有循环边可以成环。设置页可以添加连线、条件或循环。真实运行结束后,舞台继续显示那次运行,直到有新的真实运行开始。

派发没有送达(例如没有 Main 可发送,`no-sender`)不会记成已交接。聊天里写明原因,节点保持运行,下一次交接再发送。同一次交接里不会连发,避免把 429 打成一串。这次运行和它的 Bot 会留到这一步被接受、运行因别的原因失败,或被重置。回复是「could not answer your message」或「model call failed」时,该节点失败且不自动连试,后续节点不开始。成功回复里提到 429 或 rate limit 不会让节点失败。
Loading
Loading