Add background audits of the default branch's newest commit
This commit is contained in:
+10
-2
@@ -297,7 +297,7 @@ Each user has one long-running lead agent, shown in the left pane. It is not tie
|
||||
- **Messages.** `POST /api/me/lead/messages` sends the caller's message to their lead, starting a lead session when there is none. While the lead is mid-turn the message is held in `lead_inputs`; the lead's PostToolUse hook (Codex: `turn/steer` after each completed tool item; Antigravity: the `PreInvocation` hook) hands held messages over after its next tool call, like a task's queued messages (see "Agent sessions"), and any still held become its next turn. Signals are never handed over mid-turn. The composer stays enabled. `GET /api/me/lead/sessions` lists the caller's lead sessions; interrupt and compact use the usual session endpoints.
|
||||
- **Scrollback.** `GET /api/me/lead/events` returns a page of the caller's lead events in whole turns, `turns` of them (default 50): those after the `turns`+1'th `turn.ended` before `before` (or before the latest event, so the page also holds a turn still running), with `more` saying whether earlier events exist. The lead pane loads only the last page (see "Events"); when its top scrolls into view (an empty row kept first in the transcript), it fetches the page before its first event (`loadOlderLead` in `mac/Sources/Byre/Store.swift`), puts its rows in front (`TranscriptBuilder.prepend`) and scrolls back to the row that was first. A page starts just after a `turn.ended`, so rendering it from scratch matches how it drew as part of the whole history. Live events still append without limit until the app is relaunched.
|
||||
- **Signals.** Every published event goes through `Manager.Observe`. These become signals in `lead_inputs` for every user who has a lead session: `inbox.created` (a question with its choices and recommendation), `session.error` and `session.rate_limited` on a task, `inbox.updated` when a human answered a question (the signal also carries its text and the agent's recommendation, so the lead sees where the human departed from it), `task.created` (naming the TODO item it was started from, or a subtask's parent, if any), `task.merge_queued` ("<task>'s merge was requested by <login> and is held until the merge in progress in its project is done."), `task.merge_after_review` ("<task> was set by <login> to merge once the reviews of snapshot <n> finish with no findings."), `task.merge_after_review_cancelled` ("<task>'s merge after review was cancelled by <actor>."), `task.merge_after_review_dropped` ("<task> didn't merge after the review: <reason>"), `task.merge_started` ("<task> is merging, started by <login>."), `task.merged`, `task.discarded`, `turn.ended` with `summary: true` ("<task> finished a turn:" with its `result` cut to 1500 characters), `task.workflow_stage` ("<task> started stage "<stage>" of workflow "<workflow>".") and `task.workflow_finished` ("<task> finished workflow "<workflow>":" with its output cut to 1500 characters), `review.started` ("<task> started the "<stage>" review of snapshot <n>."), `review.finished` ("<task> "<stage>" review of snapshot <n>:" with the review's summary, `change` and findings, or why it failed or was skipped; `reviewLine` in `internal/agents/review.go`), `user.message` on a task from a human (see "Pane lock"), and `task.state` ("<task> state: <from> → <to>.", or "<task> state: <to>." for a new task; see "Task state in the store"). The store hands `Observe` all of one transaction's events together, and they become one signal, its `task.state` lines first, so a turn's summary always arrives with the state its turn end left the task in. A batch whose lines are all quiet is dropped instead of held (`quietSignal` in `internal/agents/lead.go`): `task.state`, `review.started`, and `task.created`, `task.merge_started`, `task.merged` and `task.discarded` by a human, unless the line carries "Your right pane is free" (see "Pane lock"). Quiet lines still ride along in a batch with anything else, and the "Tasks now" list that follows every batch shows each task's current state. The human switching the right pane is not a signal. A signal caused by the lead itself (actor `<login> (lead)`) is skipped, as is the update from an answer it gave. A task's `task.created` event carries the actor that created it, separately from its `created_by` login: tasks the lead creates with `create_task` or `start_todo` keep the human's login as `created_by` but have actor `<login> (lead)`. Whenever the lead is idle, byre sends the oldest held message from the human as its own turn, or else every held signal as one `byre` message headed "Signals since your last turn (n)", followed by "Tasks now:" with the `list_tasks` text and a line saying what the lead's right pane shows (task or project, and selected tab) and whether the human is working in it (`leadStateText` in `internal/agents/lead.go`). If collecting that fails, for example because a project's repository is gone, the batch is still sent, ending instead with "The task list and right pane state are unavailable:" and the error, which is also logged as `lead state`. Its guidance tells it not to respond to the human's own actions, and to make its final message exactly `NO_REPLY` when a batch needs nothing from it. A transcript (`quietReply` in `mac/Sources/Byre/TranscriptBuilder.swift`) never draws an `agent.message` whose last line is `NO_REPLY` (the model sometimes writes a sentence before it), and hides the live partial message while its text is still a prefix of it, so an empty one never takes up space. A lead turn that byre starts without a handoff (a signal batch or a handoff continuation) is held (`ctx.held`): its `byre` message is never drawn, the turn isn't opened, so the scanner stays off, its partial message stays hidden, and its tool calls, subagents and thinking are built into a held row list instead of the transcript. The turn is released, inserting those rows where the turn belongs, when it draws anything else: a real message, a note, or its own `turn.ended` when it ends without `NO_REPLY`. When it ends in `NO_REPLY` while still held, the held rows are dropped, so nothing of the turn ever appears; a turn that draws a real message anywhere in it keeps all of it, even when a later message is `NO_REPLY`.
|
||||
- **Tools.** `list_tasks` (each project's active tasks with their state, each subtask noting its parent, a held merge (`merge_queued_at`) and a merge after review (`merge_after_review`) each noted, then a line listing the titles of its `TODO.md` items on the default branch that no active task was started from), `read_transcript` (a task's events in brief, the last `limit` or those after `after_seq`; closed tasks too), `search_closed_tasks` (see "Closing a task"), `read_inbox`, `read_status` (the `GET /api/admin/status` body as JSON, built by the same `Server.status`, which the server hands `agents.New`), `answer_question` (an `option` or `other` response recorded with the lead's actor as its `user`; `inbox_responses.actor` and `inbox_items.resolved_by` hold actors rather than logins, so they have no foreign key to `users`; through the same checks as `POST /api/inbox/responses`), `send_message` (starts the task's agent if it has none; while it is mid-turn the message is queued through `Manager.Message` like the human's, see "Agent sessions", and the tool result says it was queued; refused while the agent is stopped after an interrupt or error, see "Agent sessions"; on a team task it posts to the board, see "Teams"), `create_task` and `start_todo` (each with an optional `workflow` to freeze and run; `create_task` also takes `agents` to start a team), `create_project` (`name` and `repo_path`, created as the user by the same `Server.createProject` as `POST /api/projects`, which the server hands `agents.New`, so it publishes the same `project.created` event, and returned as JSON), `interrupt`, `compact_task`, `take_snapshot` (see "Snapshots"), `merge_task` (optional `target`: `main`, or `parent` for a subtask, see "Subtasks"), `discard_task`, `show`, `compact_self`, `read_knowledge` (see "Knowledge base"), the note tools (see "Notes") and the workflow tools (see "Workflows"). The lead also has Claude Code's own tools and reads worktrees by path. Messages it sends task agents and its answers have actor `<login> (lead)`, so they don't make the user a participant and nobody takes them for the user's; tasks, merges and discards are done as the user. Wherever that actor is shown, it reads `<login>'s lead agent` (`actorName` in `internal/agents/lead.go` and `mac/Sources/Byre/Format.swift`): in the agent's mid-turn and board headings, in answers delivered from the inbox, in decisions the notes are built from, in `read_transcript`, and on transcript bubbles, board posts and question cards in the app. A message from the lead that starts a turn reaches the agent headed "This message is from <login>'s lead agent, not from <login>:" (`agentText`), and a queued one mid-turn as "<login>'s lead agent (not <login>) sent you this message while you were working"; the recorded `user.message` keeps the text as the lead wrote it. Its guidance says to answer a question only when the answer is clear or the human gave it, and to merge or discard only when the human asks.
|
||||
- **Tools.** `list_tasks` (each project's active tasks with their state, each subtask noting its parent, a held merge (`merge_queued_at`) and a merge after review (`merge_after_review`) each noted, then a line listing the titles of its `TODO.md` items on the default branch that no active task was started from), `read_transcript` (a task's events in brief, the last `limit` or those after `after_seq`; closed tasks too), `search_closed_tasks` (see "Closing a task"), `read_inbox`, `read_status` (the `GET /api/admin/status` body as JSON, built by the same `Server.status`, which the server hands `agents.New`), `answer_question` (an `option` or `other` response recorded with the lead's actor as its `user`; `inbox_responses.actor` and `inbox_items.resolved_by` hold actors rather than logins, so they have no foreign key to `users`; through the same checks as `POST /api/inbox/responses`), `send_message` (starts the task's agent if it has none; while it is mid-turn the message is queued through `Manager.Message` like the human's, see "Agent sessions", and the tool result says it was queued; refused while the agent is stopped after an interrupt or error, see "Agent sessions"; on a team task it posts to the board, see "Teams"), `create_task` and `start_todo` (each with an optional `workflow` to freeze and run; `create_task` also takes `agents` to start a team), `create_project` (`name` and `repo_path`, created as the user by the same `Server.createProject` as `POST /api/projects`, which the server hands `agents.New`, so it publishes the same `project.created` event, and returned as JSON), `interrupt`, `compact_task`, `take_snapshot` (see "Snapshots"), `set_audit_workflow` (see "Audits"), `merge_task` (optional `target`: `main`, or `parent` for a subtask, see "Subtasks"), `discard_task`, `show`, `compact_self`, `read_knowledge` (see "Knowledge base"), the note tools (see "Notes") and the workflow tools (see "Workflows"). The lead also has Claude Code's own tools and reads worktrees by path. Messages it sends task agents and its answers have actor `<login> (lead)`, so they don't make the user a participant and nobody takes them for the user's; tasks, merges and discards are done as the user. Wherever that actor is shown, it reads `<login>'s lead agent` (`actorName` in `internal/agents/lead.go` and `mac/Sources/Byre/Format.swift`): in the agent's mid-turn and board headings, in answers delivered from the inbox, in decisions the notes are built from, in `read_transcript`, and on transcript bubbles, board posts and question cards in the app. A message from the lead that starts a turn reaches the agent headed "This message is from <login>'s lead agent, not from <login>:" (`agentText`), and a queued one mid-turn as "<login>'s lead agent (not <login>) sent you this message while you were working"; the recorded `user.message` keeps the text as the lead wrote it. Its guidance says to answer a question only when the answer is clear or the human gave it, and to merge or discard only when the human asks.
|
||||
- **Focus.** `users.focus` holds what the user's right pane shows: `project_id`, `task_id` and `tab` (the selected one). The tabs' order is fixed; neither the lead nor the human can change it. With a `task_id`, `project_id` is always that task's project, filled in by the server; with only a `project_id`, the pane shows that project with no task, which only the Project tab has anything for. `GET /api/me/focus` reads it; `PUT /api/me/focus` and the lead's `show` set it, refusing an unknown tab, a task that isn't active or an unknown project, and record a `focus.updated` event owned by the user. The app applies the focus at startup and on every `focus.updated` it didn't cause itself; the human's own changes (⌘K, the right pane's task picker, clicking a tab, a task link, a notification) are sent with `PUT /api/me/focus`.
|
||||
- **Pane lock.** A `show` of what the pane already shows (the same task, or the same project with no task, on the same selected tab; `sameView`) tells the lead so and changes nothing. Otherwise, while the human is working in the right pane (the app reports `focused` or `drafting`, below), the lead's `show` is refused with an error telling it to say in chat what it would have shown, unless its selected tab is `project` (the fallback the lead shows when nothing needs the human, so the human being there doesn't hold it) or the pane is finished (`Manager.paneFinished`): its task is merged, discarded or merging (`merging_by` or `finalizing` set), or the human is waiting on the task's agent (`Manager.paneWaiting`): the selected tab is `transcript`, the task's implementer session is running, the app reports no unsent text in the pane, and either the `user.message` that started the running turn came from the user after the pane last moved, or the pane last moved 10 seconds ago or more (`watchHold`). The pane's last move is the last `PUT /api/me/focus` or `show` that set it (`Manager.paneMoved`, in memory, so a restart counts as long ago). For the user's own message, unsent text reported before the turn started doesn't count, since the app's report of the composer emptying on send can arrive after the message does. A `user.message` on a task from a human (not `byre`, an agent or a lead) signals every lead "<actor> sent <task> a message; its agent is working on it:" followed by the message's full text, and the sender's own lead, when its pane shows that task and is waiting that way, also gets "Your right pane is free: the human is waiting on <task>'s agent." Each `PUT /api/me/focus` from the user starts a 10-second timer (`endWatchHold`); when it fires, if the pane hasn't moved since, the app still reports the human in it, the user hasn't messaged the task since the move (that already freed it, above), and the pane is waiting that way, the lead gets the signal "Your right pane is free: the human has watched <task>'s agent work for 10 seconds without typing." The lead's own `show` starts no timer. When a `task.merge_started`, `task.merged` or `task.discarded` event leaves a user's focused task finished that way, that user's signal for it gets a line "Your right pane is free: <task> is merging." (or "is merged."/"is discarded."). The lead's guidance tells it to show the next thing that needs the human on that line, or, when nothing does, the project's Project tab with a short status in chat: one line per active task, then up to three TODO items it would start next, each with a reason, starting one only when the human picks it. It writes each item as a link to `#/todo/<project_id>/<URL-encoded title>`; Markdown views draw that (`todo` in `mac/Sources/Byre/MarkdownView.swift`) as the title followed by `start` and `discuss` actions, as accent link text so the line stays selectable, that call `POST /api/projects/{id}/todo/tasks` with the user's default model, as the Project tab's do, or, once an active task was started from that item, the task's colour, dot and state, linking to it. When the lead suggests new work that isn't in TODO.md, its guidance tells it to write a link to `#/suggest/<project_id>/<URL-encoded title>/<URL-encoded brief>`, drawn by the same code with `start` and `discuss` buttons: `start` calls `POST /api/projects/{id}/tasks` with the title, the brief as `prompt` and the user's default model, which checks the user's git identity, creates the task and starts its agent from one request, with no lead turn in between; `discuss` sends the lead `POST /api/me/lead/messages` asking to discuss the suggestion, quoting its title and brief (with the user's default model only when the user has no live lead session). Once an active task in that project has the suggestion's title, it shows that task's state the same way. A chat view refills them whenever its transcript grows or tasks or sessions change. When that status says nothing new since its last one, it replies `NO_REPLY` instead. The app sends `PUT /api/me/pane-focus` with `focused` and `drafting` whenever either changes (`PaneFocus` in `mac/Sources/Byre/PaneFocus.swift`): `focused` is true while the window is key, the app is active and the keyboard focus or the last click is inside the right pane, so a click anywhere in it counts, not only on inputs and buttons; `drafting` while any text field in the right pane other than the terminal holds non-blank text. The window reports to the server it shows. It sends false on deactivating, switching to another server, Forget Server, closing the window and quit, and resends true when its event stream reconnects. byre keeps both flags and the time of the last report in memory per user (`Manager.paneFocus`, `Manager.paneReported`), so they start false after a restart until the app resends them.
|
||||
- **Move hold.** Each `PUT /api/me/focus` from the user (switching task or tab, by any means: the switcher, a tab, a link or a notification) records the time in `Manager.humanMoved`, and for 10 seconds after it (`moveHold`) every `show` that would change the view is refused, whatever the pane lock above would allow, with an error giving how long ago the move was. A `show` of what the pane already shows changes nothing and says so. A refusal also marks the user in `Manager.moveRefused`, which each move by the user clears. When the 10 seconds pass without another move by the user (`endMoveHold`, on a timer), byre clears both and, only if a `show` was refused since that move, sends the lead the signal "The move hold is over: …"; its guidance tells it to show what it held back if the pane's other rules allow, or reply `NO_REPLY`. Because only a refusal arranges the signal, the lead's system prompt and the `show` tool's description tell it to call `show` at once whenever the user asks to see something or it wants to show something, never holding back in expectation of a refusal. It is in memory, so a restart clears it.
|
||||
@@ -316,7 +316,7 @@ A project's `TODO.md` is read from the root of its default branch in the project
|
||||
|
||||
The title is the item's identity. `POST /api/projects/{id}/todo/tasks` with a `title` re-reads `TODO.md`, refuses an unknown title (404) or one an active task already has (409), creates a task from the default branch with that title and `todo_item` (the `tasks.todo_item` column, empty for other tasks), and starts its session as the requesting user with `todoPrompt` (`internal/agents/tasks.go`): the item as written, and an instruction to remove it from `TODO.md` in the worktree when the work is done. With `discuss: true` the prompt adds that the human wants to discuss the item first: the agent zooms out first, reads what it needs and gives its view on whether the item should be built at all and in what shape (the problem it solves, alternatives including a smaller change or nothing, its approach, trade-offs and doubts), talks it over in its final messages and in as many `ask_human` questions as help, each about those broad choices rather than build details and free to build on earlier answers, and changes nothing until told to build it. The agent's guidance names the item too (`taskGuidance`, see "Agent sessions"), so the instruction survives compaction and resumes. The agent removes the item on its own branch, so the removal lands on the default branch in the same squash commit as the work.
|
||||
|
||||
The **Project** tab is the dashboard of the right pane's project (the focused task's, or the project focused with no task). Under **Tasks** it lists the project's active tasks in the server's order (newest first), each with its task state (see "Task state") and open inbox item count, as links that point the right pane at the task's transcript; the focused task is highlighted. Under **TODO** it lists the `TODO.md` items, grouped by section, with each body rendered as Markdown. An item with an active task whose `todo_item` equals its title shows that task's state as a link to the task instead of the **Start** and **Discuss** buttons. **Start** posts the item with the composer's default model; **Discuss** posts it the same way with `discuss: true`. Neither moves the right pane (see "Start hold"). The task list follows byre's live state; the tab fetches the project's `TODO.md` when it opens, when the right pane moves to another project, when the project list changes, when the SSE stream reconnects, and on `task.merged`; a change pushed to the default branch outside byre shows the next time one of those happens.
|
||||
The **Project** tab is the dashboard of the right pane's project (the focused task's, or the project focused with no task). Under **Tasks** it lists the project's active tasks in the server's order (newest first), each with its task state (see "Task state") and open inbox item count, as links that point the right pane at the task's transcript; the focused task is highlighted. Background audit tasks follow as muted rows (see "Audits"). The header carries the audits and reviews menus. Under **TODO** it lists the `TODO.md` items, grouped by section, with each body rendered as Markdown. An item with an active task whose `todo_item` equals its title shows that task's state as a link to the task instead of the **Start** and **Discuss** buttons. **Start** posts the item with the composer's default model; **Discuss** posts it the same way with `discuss: true`. Neither moves the right pane (see "Start hold"). The task list follows byre's live state; the tab fetches the project's `TODO.md` when it opens, when the right pane moves to another project, when the project list changes, when the SSE stream reconnects, and on `task.merged`; a change pushed to the default branch outside byre shows the next time one of those happens.
|
||||
|
||||
## Inbox
|
||||
|
||||
@@ -484,6 +484,14 @@ Every new snapshot of a task is reviewed by its project's review workflow: each
|
||||
- **Agent responses.** The message delivering findings to the agent numbers them and tells it to answer each with the task agent's `respond_to_finding` MCP tool (`respondToFindingTool` in `internal/agents/review.go`) before changing anything: `snapshot`, `reviewer` (the stage name), `finding` (from 1 in that reviewer's review), `response` (`fixing` or `ignoring`) and a one-line `reason`. byre records it in the review's `finding_responses` (the `reviews.finding_responses` column, a JSON object from index to `{response, reason}`; a later call for the same finding replaces it) and emits `review.finding` with actor `agent`. The answer also clears the finding's `fixed` state, since it answers that fix request: an ignore after the user's **fix** brings **fix** back so the user can overrule it again. A user's `ignored` stays. A call naming a snapshot and reviewer with no `done` review, or a finding it doesn't have, is refused. On the finding, an answer shows as "agent fixing: …" or "agent ignoring: …" and dims the finding's diff, detail and screenshots. Fixing takes away both buttons; ignoring keeps **fix**, so the user can overrule it, and drops **ignore**. The tool call itself is its own transcript row (not folded into a group of tool calls): what the agent answered for which finding, its title and reason, a **show** button that scrolls to the finding, and, for an ignore, the same **fix** button while the user hasn't answered the finding. A call that fails is hidden. The Merge confirmation's review warning counts findings the user hasn't ignored, whatever the agent answered.
|
||||
- **State.** Snapshot rows carry no review mark; the reviewer rows show each review. While any of a task's reviews is `running` and its agent is idle, the task's state is `reviewing` (see "Task state in the store"), which the task switcher and the Project tab show as **Reviewing** (see "Task state"). `GET /api/tasks/{taskId}/reviews` lists a task's reviews oldest first. `sqlite3 byre.db "SELECT id, task_id, snapshot_n, stage, vendor, state, error FROM reviews ORDER BY created_at DESC LIMIT 20"` shows recent ones.
|
||||
|
||||
## Audits
|
||||
|
||||
Reviews look at one task's change; an audit checks the whole codebase as it stands on the default branch, against its standards and expectations (`internal/agents/audit.go`). A project's audit workflow is any saved workflow, named by `projects.audit_workflow` (`audit_workflow` on the Project, empty for none), set with `PUT /api/projects/{projectId}/audit-workflow` (`{workflow}`, the same `ProjectWorkflow` body as the review workflow's; a missing workflow is refused, and so is any workflow while the project's creator, whom audits run as, has no git identity), from the Project tab's audits menu (beside the reviews menu, one shared `ProjectWorkflowMenu` in `mac/Sources/Byre/Project.swift`) or the lead's `set_audit_workflow`, recording `project.updated`. Renaming or deleting a workflow repoints or clears `audit_workflow` as it does `review_workflow` (`repointProjects` in `internal/store/workflow.go`).
|
||||
|
||||
- **Starting.** An audit is an ordinary run of the workflow (see "Workflows"), titled `Audit of <short commit>`, run as the project's creator with actor `byre`, from the default branch's commit in the project's checkout (`git.BranchHead`), with a brief (`auditBrief`) saying it audits that commit rather than a change, that it runs in the background, to finish each stage with `finish_stage` as usual, and to answer byre's ready prompt with `suggest_discard` when nothing needed changing. Each of its stage tasks records that commit in `tasks.audit` (`audit` on the Task; a fork of one doesn't), and a stage that would start from the default branch (one after several stages) starts from that commit instead, so every stage audits the same tree. A project has at most one audit at a time and audits only the newest commit, so it doesn't run on every revision but the newest one always gets audited eventually: `startAudit`, under `Manager.auditMu`, starts one when the project has an audit workflow, no active task with `audit` set, and the branch's commit differs from the newest audit task's `audit` (`Store.LastAudit`). It runs at startup for every project, when the audit workflow is set, and on every `task.merged`, `task.merged_to_main` and `task.discarded` in the project (`Manager.Observe`), which covers the default branch moving through byre and an audit ending. A workflow that fails to start is logged as `start audit`, and each of its tasks, the one whose agent failed to start included, gets a `session.error` saying so, which brings it out of the background for the human and the lead. Those tasks stay active, so no other audit starts until someone discards them, and they keep `audit`, so the discard doesn't retry that commit; the branch's next commit does. byre never fetches for an audit, so a commit pushed to the default branch outside byre is audited only once a byre Merge or `merge_to_main` has fetched it into the checkout. `sqlite3 byre.db "SELECT id, status, audit FROM tasks WHERE audit != '' ORDER BY created_at DESC LIMIT 10"` shows recent audits.
|
||||
- **Background.** A Task's `background` is true while it has `audit` set and no `inbox.created`, `task.ready`, `session.error` or `session.rate_limited` event, that is until it asks for attention (`backgroundExpr` in `internal/store/store.go`). While it is: the ⌘K switcher leaves it out; the Project tab draws it after the task list as one muted row, its title and state in grey (`BackgroundRow`), which opens it; its turn summaries, discard suggestions and finished workflow don't notify; the lead gets no signals about it (`dropBackground` in `internal/agents/lead.go`) and `list_tasks` leaves it out. After that it is an ordinary task everywhere, for good. A failed review of its latest snapshot, which keeps byre from asking whether it is ready, also brings it out: when the ready check (`tryPromptReady`) finds one on a background task, byre records a `session.error` on the task naming the review (`auditError`), so it doesn't sit hidden and block later audits.
|
||||
- **Dismissing itself.** When a background task calls `suggest_discard` once its run has a `task.workflow_finished` event (byre's ready prompt offers it, and `readyPromptUnchanged` when the audit changed nothing), byre discards it at once as `byre` (`dismissAudit`), so it never shows **Ready to discard**. A suggestion before the run finishes isn't acted on, so a discarded stage can't stall the run's other stages. An audit that asked for attention first is left for the human like any task. At startup, before starting audits, byre retries the dismissal for every active background task left in `discard_suggested` by a restart (`auditAll`).
|
||||
|
||||
## Workflows
|
||||
|
||||
A workflow is a named tree of steps: stages, each a stage name, a model and the stage agent's prompt written out in full, nested in `order` and `parallel` blocks. Every stage runs as its own task, once the step before it has finished, so a stage's output can go to several branches of a `parallel` (fan-out) and the stage after a `parallel` waits for all its branches (fan-in). Workflows live in byre's database, in the `workflows` table (`name`, and the definition's JSON as `definition`), shared by every project and never in a repo. byre stores, validates, exports, imports and freezes them, and runs each run's frozen copy stage by stage (`internal/agents/workflows.go`; the graph helpers `After`, `Next`, `Starts` and `FromMain` are in `internal/workflow/workflow.go`). The human starts one from the task switcher (see "Starting"), or from the left pane's Workflows tab, which lists them and hands a run to the lead (see "UI"); the human edits them in that tab (see "UI"), and the lead with its tools, drawing a workflow for the human as a Mermaid flowchart of its stage names, which its chat renders (see "Inbox"). A run's progress is shown read-only in the stage tasks' Workflow tab (see "Progress" below).
|
||||
|
||||
Reference in New Issue
Block a user