Add team tasks: several agents in one worktree sharing a board

This commit is contained in:
Ian Gulliver
2026-10-18 08:48:57 -07:00
parent 7f62df19d2
commit 6bcde13b2f
31 changed files with 1392 additions and 439 deletions
+18 -19
View File
@@ -20,8 +20,14 @@ import (
const maxMergeTitle = 72
const mergeInMarker = " into this branch: run `git merge --no-ff --no-commit "
func isMergeInPrompt(actor, text string) bool {
return actor == "byre" && strings.Contains(text, mergeInMarker)
}
func mergeInPrompt(target string) string {
return fmt.Sprintf("Merge %[1]s into this branch: run `git merge --no-ff --no-commit %[1]s`. "+
return fmt.Sprintf("Merge %[1]s"+mergeInMarker+"%[1]s`. "+
"Resolve any conflicts keeping both sides' intent; regenerate generated files instead of hand-merging them. "+
"Check that the build and tests pass. Leave the merge uncommitted: byre concludes it when the turn ends. "+
"If resolving the conflicts took substantive changes rather than combining both sides as written, stop there: "+
@@ -34,18 +40,14 @@ func (m *Manager) MergeIn(ctx context.Context, task api.Task, user api.User, tar
}
func (m *Manager) sendMergeIn(ctx context.Context, task api.Task, user api.User, prompt string) (api.AgentSession, error) {
sessions, err := m.store.ListSessions(ctx, task.Id)
sess, found, err := m.liveSession(ctx, task.Id)
if err != nil {
return api.AgentSession{}, err
}
for _, sess := range sessions {
if sess.State != api.AgentSessionStateCompleted {
return m.Send(ctx, sess.Id, "byre", prompt, "")
}
if found {
return m.Send(ctx, sess.Id, "byre", prompt, "")
}
return m.create(ctx, task, user, prompt, "", "byre", nil)
return m.create(ctx, task, user, sess.Member, prompt, "", "byre", nil)
}
func (m *Manager) Merge(ctx context.Context, task api.Task, project api.Project, user api.User, title string) (api.Task, error) {
@@ -140,19 +142,12 @@ func (m *Manager) startMerge(ctx context.Context, pm *projectMerge, task api.Tas
}
func (m *Manager) mergeSession(ctx context.Context, task api.Task, user api.User) (api.AgentSession, error) {
sessions, err := m.store.ListSessions(ctx, task.Id)
sess, found, err := m.liveSession(ctx, task.Id)
if err != nil {
return api.AgentSession{}, err
}
sess := api.AgentSession{}
for _, s := range sessions {
if s.State != api.AgentSessionStateCompleted {
sess = s
}
}
if sess.Id == "" {
sess, err = m.newSession(ctx, task, user, "")
if !found {
sess, err = m.newSession(ctx, task, user, sess.Member, "")
if err != nil {
return api.AgentSession{}, err
}
@@ -413,6 +408,7 @@ func (m *Manager) finishMerge(l *live, login string) {
ctx := context.Background()
err := m.squashAndClose(ctx, l.taskID, login)
m.endMergeTurn(l.id, false)
if err == nil {
return
}
@@ -655,6 +651,9 @@ func (m *Manager) stopMerge(ctx context.Context, l *live, raw json.RawMessage) (
return "", fmt.Errorf("%w: reason must say what the resolution changed", ErrInvalid)
}
if !m.leadsTask(ctx, l.taskID, l.id) {
return "", fmt.Errorf("%w: only the team's first agent runs the merge, so only it can stop it", ErrConflict)
}
if m.takeMerge(l.taskID) == "" {
return "No merge is pending for this task; just end your turn.", nil
}