Lock the right pane while focused, add project focus, quiet lead

This commit is contained in:
Ian Gulliver
2026-10-15 09:59:13 -07:00
parent bdd1ae48f0
commit fdd005b591
32 changed files with 576 additions and 227 deletions
+12 -23
View File
@@ -15,8 +15,14 @@ import (
)
const leadGuidance = `You are the human's lead agent in byre, shown in the left pane of byre's web UI. The human talks to you there, and you coordinate every task agent on their behalf. You don't do task work yourself: task agents work in their own worktrees, and you read, direct and report on them.
- Signals: byre sends you, as messages from byre, what happens across tasks: questions, turn reports and errors that task agents file, questions the human answered, tasks merged or discarded, and the human switching the right pane. Several may arrive in one message. Messages from the human arrive separately.
- Direct the human's attention. The right pane always shows one task; point it with show (the task, the tabs to put first, the tab to select). When something needs the human, show it and say in a sentence or two what it is and what you recommend. Don't move the pane for things that don't need them, and don't pull it away from what they are looking at unless the new thing is more urgent.
- Signals: byre sends you, as messages from byre, what happens across tasks: questions, turn reports and errors that task agents file, questions the human answered, tasks merged or discarded. Several may arrive in one message. Messages from the human arrive separately.
- Don't respond to what the human just did. A signal about their own action (a question they answered, a task they merged or discarded) tells them nothing new: don't report, confirm or comment on it. Act on it only if it changes what you are doing, and then end the turn with one short line at most. When a batch of signals needs nothing from you to the human, make your final message exactly NO_REPLY and nothing else; byre folds that turn away so the human doesn't see it.
- Steering the right pane is a core part of your job, as important as what you write. The right pane shows one task, or a project with no task; point it with show (the task or project, the tabs to put first, the tab to select). By default, keep it on whatever you and the human are discussing, so they can see it while they read your reply; don't make them go looking for it. Call show in the same turn you bring something up:
- Discussing, picking or starting TODO items: the project's todo tab (show with project_id when no task of that project is involved).
- A question or report that needs the human: that task's inbox tab, and say in a sentence or two what it is and what you recommend.
- Discussing a task's work, what its agent did or is doing, or whether it is ready to merge: its transcript tab. A specific file in its worktree: its files tab.
- The changes tab only when the human asks to see the diff; they rarely want it.
- Don't move the pane for things that don't need the human, and don't pull it away from what they are looking at unless the new thing is more urgent. While the human is working in the right pane, show is refused: say in chat what you would have shown, and carry on.
- Questions from task agents: answer one yourself with answer_question only when the answer is clear, for example the agent's recommendation is plainly right given what the human has said, or the human already decided it. Otherwise show it to the human with your recommendation and wait. When the human tells you an answer, give it with answer_question. Say which questions you answered and how.
- Reports: read them and tell the human only what matters: work finished and ready to merge, problems, decisions made. Stay quiet about routine progress.
- 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.
@@ -245,7 +251,7 @@ func (m *Manager) leadTurnEnded(ctx context.Context, l *live, res claude.Result)
func (m *Manager) Observe(ev api.Event) {
switch ev.Kind {
case "inbox.created", "inbox.updated", "task.merged", "task.discarded", "focus.updated":
case "inbox.created", "inbox.updated", "task.merged", "task.discarded":
default:
return
}
@@ -271,15 +277,9 @@ func (m *Manager) signalLoop() {
}
func (m *Manager) signal(ctx context.Context, ev api.Event) error {
owners := []string{}
if ev.Owner != nil {
owners = append(owners, *ev.Owner)
} else {
all, err := m.store.LeadOwners(ctx)
if err != nil {
return err
}
owners = all
owners, err := m.store.LeadOwners(ctx)
if err != nil {
return err
}
text, err := m.signalText(ctx, ev)
@@ -334,17 +334,6 @@ func (m *Manager) signalText(ctx context.Context, ev api.Event) (string, error)
case "task.discarded":
return fmt.Sprintf("%s was discarded by %s.", m.taskLabel(ctx, deref(ev.TaskId)), ev.Actor), nil
case "focus.updated":
f := api.Focus{}
err := remarshal(ev.Payload, &f)
if err != nil {
return "", err
}
if f.TaskId == nil {
return "The human cleared the right pane.", nil
}
return fmt.Sprintf("The human switched the right pane to %s, tab %s.", m.taskLabel(ctx, *f.TaskId), f.Tab), nil
}
return "", nil
}