2026-10-21 11:28:07 -07:00
package agents
import (
"context"
2026-10-21 12:54:34 -07:00
"fmt"
2026-10-21 11:28:07 -07:00
"log/slog"
2026-10-21 15:15:20 -07:00
"time"
2026-10-21 11:28:07 -07:00
)
const abandonedMergeText = "The Merge turn ended unfinished (interrupted, failed to start, or with no final message), so byre didn't squash or push the task. Merge again to retry."
func ( m * Manager ) poke () {
select {
case m . pokes <- struct {}{}:
default :
}
}
func ( m * Manager ) coordinate () {
2026-10-21 11:53:37 -07:00
started := map [ string ] bool {}
2026-10-21 15:15:20 -07:00
timer := time . NewTimer ( time . Hour )
timer . Stop ()
2026-10-21 11:28:07 -07:00
for {
select {
case <- m . watchStop :
return
case <- m . pokes :
2026-10-21 15:15:20 -07:00
case <- timer . C :
}
next := m . coordinatePass ( context . Background (), started )
timer . Stop ()
if next != nil {
timer . Reset ( time . Until ( * next ))
2026-10-21 11:28:07 -07:00
}
}
}
2026-10-21 15:15:20 -07:00
func ( m * Manager ) coordinatePass ( ctx context . Context , started map [ string ] bool ) * time . Time {
2026-10-21 11:53:37 -07:00
work , err := m . store . Coordinate ( ctx )
2026-10-21 11:28:07 -07:00
if err != nil {
2026-10-21 11:53:37 -07:00
slog . Error ( "coordinate" , "err" , err )
2026-10-21 15:15:20 -07:00
retry := time . Now (). Add ( time . Minute )
return & retry
2026-10-21 11:28:07 -07:00
}
2026-10-21 11:53:37 -07:00
for _ , id := range work . AbandonedMerges {
2026-10-21 11:28:07 -07:00
slog . Info ( "merge turn ended unfinished" , "task" , id )
m . fileError ( ctx , id , abandonedMergeText )
m . deliverIdle ( ctx , id )
}
2026-10-21 11:53:37 -07:00
for _ , c := range work . StartMerges {
2026-10-21 11:28:07 -07:00
m . startClaimedMerge ( ctx , c )
}
2026-10-21 11:53:37 -07:00
for _ , id := range work . StartDiscards {
m . startDiscard ( ctx , id )
}
2026-10-21 12:54:34 -07:00
for _ , r := range work . EndReviewers {
go m . cleanUpReviewer ( r )
}
2026-10-21 11:53:37 -07:00
2026-10-21 11:28:07 -07:00
current := map [ string ] bool {}
2026-10-21 11:53:37 -07:00
once := func ( key string , run func ()) {
current [ key ] = true
if started [ key ] {
return
2026-10-21 11:28:07 -07:00
}
2026-10-21 11:53:37 -07:00
started [ key ] = true
go run ()
}
for _ , f := range work . FinalizeMerges {
once ( "merge:" + f . Task . Id , func () { m . finalizeMerge ( f ) })
}
for _ , id := range work . FinishDiscards {
once ( "discard:" + id , func () { m . finishDiscard ( id ) })
2026-10-21 11:28:07 -07:00
}
2026-10-21 12:54:34 -07:00
for _ , r := range work . StartReviews {
once ( fmt . Sprintf ( "review:%s:%d" , r . TaskID , r . N ), func () { m . startReviews ( r ) })
}
for _ , s := range work . StartStages {
once ( fmt . Sprintf ( "stage:%s:%d:%s" , s . TaskID , s . SnapshotN , s . Stage ), func () { m . startReviewStage ( s ) })
}
for _ , r := range work . SettleReviews {
once ( fmt . Sprintf ( "settle:%s:%d" , r . TaskID , r . N ), func () { m . settleReviews ( r ) })
}
2026-10-21 13:11:03 -07:00
for _ , id := range work . AdvanceStages {
once ( "advance:" + id , func () { m . advanceStage ( id ) })
}
2026-10-21 13:59:37 -07:00
for _ , d := range work . LeadDeliveries {
once ( fmt . Sprintf ( "lead:%s:%d:%d" , d . Owner , d . First , d . Last ), func () { m . deliverLead ( d . Owner ) })
}
for _ , q := range work . QueuedDelivery {
once ( fmt . Sprintf ( "queue:%s:%d:%s" , q . SessionID , q . Input , q . Response ), func () { m . deliverQueuedTurn ( q . SessionID ) })
}
2026-10-21 12:54:34 -07:00
for _ , p := range work . ReadyPrompts {
once ( "ready:" + p . TaskID , func () { m . promptReady ( p ) })
}
2026-10-21 15:15:20 -07:00
for _ , sc := range work . DueSchedules {
once ( fmt . Sprintf ( "schedule:%s:%d" , sc . Id , sc . NextRun . UnixNano ()), func () { m . runSchedule ( sc ) })
}
2026-10-21 11:53:37 -07:00
for key := range started {
if ! current [ key ] {
delete ( started , key )
2026-10-21 11:28:07 -07:00
}
}
2026-10-21 15:15:20 -07:00
return work . NextSchedule
2026-10-21 11:28:07 -07:00
}