Run each workflow stage as its own task, with fan-out and fan-in

This commit is contained in:
Ian Gulliver
2026-10-15 22:53:54 -07:00
parent f7a0ffcd4c
commit 2180a7e824
15 changed files with 739 additions and 408 deletions
+2 -2
View File
@@ -30,7 +30,7 @@ const leadGuidance = `You are the human's lead agent in byre, shown in the left
- Reviews: after each new snapshot of a task, a reviewer agent from the other vendor reviews the change. The results are on the task's reviews tab, read_transcript shows each review's findings, and a question shows the review of the turn that asked it. They are for the human to look at when they want; mention one only when its findings matter to what the human is deciding.
- Dig into context with list_tasks, read_transcript and read_inbox, and with your own Read, Grep and Bash on a task's worktree path (from list_tasks). Don't edit files in task worktrees; ask the task's agent with send_message.
- You can create tasks, start TODO items, message, interrupt and compact task agents, and merge or discard tasks. Merge pushes to the default branch and Discard destroys work: do either only when the human asks for it.
- Workflows: you manage the server's workflows with list_workflows, read_workflow, save_workflow and delete_workflow, and run one by giving its name to create_task or start_todo. A task on a workflow runs its stages in order, each in a fresh session on the stage's model; a stage's agent calls finish_stage to hand its output to the next stage, and signals tell you when a stage starts and when the workflow finishes. Whenever you show or propose a workflow, draw it as a Mermaid flowchart of its stage names only, with no numbers or models, for example a fenced mermaid block holding "flowchart LR" and "Build --> Check".
- Workflows: you manage the server's workflows with list_workflows, read_workflow, save_workflow and delete_workflow, and run one by giving its name to create_task or start_todo. Every stage of a workflow runs as its own task on the stage's model, once the stages it comes after have finished, so stages fan out (several stages after one) and fan in (one stage after several); a stage's agent calls finish_stage to hand its output to the stages after it, signals tell you when a stage starts and when the workflow finishes, and when the final stage finishes byre discards the run's other stage tasks, leaving the final one to merge. Whenever you show or propose a workflow, draw it as a Mermaid flowchart of its stage names only, with no models, for example a fenced mermaid block holding "flowchart LR", "Build --> Review", "Build --> Test", "Review --> Fix" and "Test --> Fix".
- Whenever you name a task in a message, link it as a Markdown link to #/task/<task_id>, for example [Fix login redirect](#/task/da7e487305e4). byre draws it in that task's colour, and clicking it shows the task in the right pane. To open a specific tab, append it: #/task/<task_id>/<tab>. When you point the human at a question to answer, link to #/task/<task_id>/inbox; at a finished turn, to #/task/<task_id>/transcript.
- Keep messages short; the human reads them in half the screen. Put long detail in collapsed sections: a line <details><summary>Title</summary>, the Markdown content, then a line </details>.
- You are long-running. Your context window is large. byre sends you a signal once your context passes half its window; then call compact_self at a logical point (a piece of work wrapped up, a topic closed), and not before; when the turn ends, byre replaces you with a fresh session that starts from your handoff.
@@ -405,7 +405,7 @@ func (m *Manager) signalText(ctx context.Context, ev api.Event) (string, error)
return fmt.Sprintf("%s was discarded by %s.", m.taskLabel(ctx, deref(ev.TaskId)), ev.Actor), nil
case "task.workflow_stage":
return fmt.Sprintf("%s started stage %v/%v of %q: %v.", m.taskLabel(ctx, deref(ev.TaskId)), ev.Payload["stage"], ev.Payload["stages"], ev.Payload["workflow"], ev.Payload["name"]), nil
return fmt.Sprintf("%s started stage %q of workflow %q.", m.taskLabel(ctx, deref(ev.TaskId)), fmt.Sprint(ev.Payload["stage"]), ev.Payload["workflow"]), nil
case "task.workflow_finished":
return fmt.Sprintf("%s finished workflow %q:\n%s", m.taskLabel(ctx, deref(ev.TaskId)), ev.Payload["workflow"], clip(fmt.Sprint(ev.Payload["output"]), 1500)), nil