2026-10-20 21:08:31 -07:00
package agents
import (
"context"
"fmt"
"log/slog"
"strings"
"g.fc.run/ai/byre/internal/api"
"g.fc.run/ai/byre/internal/git"
)
func ( m * Manager ) SetAuditWorkflow ( ctx context . Context , projectID , name , actor string ) ( api . Project , error ) {
name = strings . TrimSpace ( name )
if name != "" {
_ , err := m . store . GetWorkflow ( ctx , name )
if err != nil {
return api . Project {}, err
}
project , err := m . store . GetProject ( ctx , projectID )
if err != nil {
return api . Project {}, err
}
creator , err := m . store . GetUser ( ctx , project . CreatedBy )
if err != nil {
return api . Project {}, err
}
if creator . GitName == "" || creator . GitEmail == "" {
return api . Project {}, fmt . Errorf ( "%w: audits run as the project's creator %s, who has no git identity" , ErrInvalid , creator . Login )
}
}
p , err := m . store . SetAuditWorkflow ( ctx , projectID , name , actor )
if err != nil {
return api . Project {}, err
}
go m . audit ( p . Id )
return p , nil
}
func ( m * Manager ) auditAll ( ctx context . Context ) {
projects , err := m . store . ListProjects ( ctx )
if err != nil {
slog . Error ( "start audits" , "err" , err )
return
}
for _ , p := range projects {
tasks , err := m . store . ListTasks ( ctx , p . Id )
if err != nil {
slog . Error ( "start audits" , "project" , p . Id , "err" , err )
continue
}
for _ , t := range tasks {
if t . Status == api . TaskStatusActive && t . Background && t . State == api . TaskStateDiscardSuggested {
m . dismissAudit ( t . Id )
}
}
m . audit ( p . Id )
}
}
func ( m * Manager ) audit ( projectID string ) {
err := m . startAudit ( context . Background (), projectID )
if err != nil {
slog . Error ( "start audit" , "project" , projectID , "err" , err )
}
}
func ( m * Manager ) startAudit ( ctx context . Context , projectID string ) error {
m . auditMu . Lock ()
defer m . auditMu . Unlock ()
project , err := m . store . GetProject ( ctx , projectID )
if err != nil || project . AuditWorkflow == "" {
return err
}
last , active , err := m . store . LastAudit ( ctx , projectID )
if err != nil || active {
return err
}
head , err := git . BranchHead ( project . RepoPath , project . DefaultBranch )
if err != nil {
return err
}
if head == last {
return nil
}
creator , err := m . store . GetUser ( ctx , project . CreatedBy )
if err != nil {
return err
}
tasks , err := m . StartWorkflow ( ctx , project , creator , "Audit of " + short ( head ), head , "byre" , project . AuditWorkflow , auditBrief ( project . DefaultBranch , head ), nil , head )
if err == nil {
slog . Info ( "audit started" , "project" , projectID , "commit" , head , "workflow" , project . AuditWorkflow )
return nil
}
for _ , t := range tasks {
2026-10-21 09:02:09 -07:00
if t . WorkflowStage == nil {
continue
}
2026-10-20 21:08:31 -07:00
m . auditError ( ctx , t , fmt . Sprintf ( "The audit of %s didn't start: %v. Discard this task to let audits continue; the next commit on %s starts one." , short ( head ), err , project . DefaultBranch ))
}
return err
}
func ( m * Manager ) auditError ( ctx context . Context , task api . Task , msg string ) {
err := m . store . AppendEvent ( ctx , api . Event {
Kind : "session.error" ,
ProjectId : & task . ProjectId ,
TaskId : & task . Id ,
Actor : "byre" ,
Payload : map [ string ] any { "error" : msg },
})
if err != nil {
slog . Error ( "record audit error" , "task" , task . Id , "err" , err )
}
}
func auditBrief ( branch , commit string ) string {
return fmt . Sprintf ( "Audit of %s at %s. This isn't a review of a change: check the whole codebase as it stands against the project's standards and expectations, and fix what falls short. " +
"byre runs this task in the background: the human doesn't see it until you ask a question, call ready_for_merge or hit an error. " +
2026-10-21 08:46:51 -07:00
"Finish your stage with finish_stage as usual, even when you found nothing. Once your stage has finished and its review has come back, byre asks whether the task is ready to merge;" +
2026-10-20 21:08:31 -07:00
"if nothing needed changing, answer with suggest_discard, and byre discards the task without bothering the human." , branch , commit )
}
func ( m * Manager ) dismissAudit ( taskID string ) {
ctx := context . Background ()
task , err := m . store . GetTask ( ctx , taskID )
if err != nil {
slog . Error ( "dismiss audit" , "task" , taskID , "err" , err )
return
}
2026-10-21 09:02:09 -07:00
if ! task . Background || task . Status != api . TaskStatusActive || task . WorkflowStage == nil {
2026-10-20 21:08:31 -07:00
return
}
2026-10-21 08:46:51 -07:00
output , _ , err := m . store . StageResult ( ctx , taskID )
2026-10-20 21:08:31 -07:00
if err != nil {
slog . Error ( "dismiss audit" , "task" , taskID , "err" , err )
return
}
2026-10-21 08:46:51 -07:00
if output == "" {
2026-10-20 21:08:31 -07:00
return
}
_ , err = m . Discard ( ctx , task , "byre" )
if err != nil {
slog . Error ( "dismiss audit" , "task" , taskID , "err" , err )
return
}
slog . Info ( "audit dismissed itself" , "task" , taskID )
}