2026-10-21 11:48:21 -07:00
package store
import (
"context"
"database/sql"
"errors"
"reflect"
"g.fc.run/ai/byre/internal/api"
)
const mergeFactsColumns = `unmerged_commits, unmerged_files, unreviewed_changes, review_snapshot, review_count, review_running, review_failed, review_findings,
(SELECT json_group_array(json_object('login', login, 'display_name', display_name, 'git_name', git_name, 'git_email', git_email))
FROM (SELECT * FROM task_participants WHERE task_participants.task_id = tasks.id ORDER BY position))`
type GitFacts struct {
UnmergedCommits int
UnmergedFiles int
UnreviewedChanges bool
}
func ( s * Store ) SettleMergeFacts ( ctx context . Context , taskID string , g GitFacts ) error {
return s . write ( ctx , func ( tx * sql . Tx , emit emitFunc ) error {
t , err := scanTask ( tx . QueryRowContext ( ctx , `SELECT ` + taskColumns + ` FROM tasks WHERE id = ?` , taskID ))
if errors . Is ( err , sql . ErrNoRows ) {
return ErrNotFound
}
if err != nil {
return err
}
if t . Status != api . TaskStatusActive {
return nil
}
f := api . MergeFacts { UnmergedCommits : g . UnmergedCommits , UnmergedFiles : g . UnmergedFiles , UnreviewedChanges : g . UnreviewedChanges }
2026-10-21 11:55:19 -07:00
err = tx . QueryRowContext ( ctx , `SELECT coalesce(max(n), 0) FROM snapshots WHERE task_id = ? AND trigger NOT IN ` + cutPointTriggers , taskID ). Scan ( & f . ReviewSnapshot )
2026-10-21 11:48:21 -07:00
if err != nil {
return err
}
err = tx . QueryRowContext ( ctx , `
SELECT count(*), coalesce(max(state = ?), 0), coalesce(max(state = ?), 0),
coalesce(sum(CASE WHEN state = ? THEN (
SELECT count(*) FROM json_each(nullif(reviews.result, ''), '$.findings') f
WHERE coalesce(json_extract(reviews.finding_states, '$."' || f.key || '"'), '') != ?
) END), 0)
FROM reviews WHERE task_id = ? AND snapshot_n = ?
` , string ( api . ReviewStateRunning ), string ( api . ReviewStateFailed ), string ( api . ReviewStateDone ), string ( api . FindingIgnored ), taskID , f . ReviewSnapshot ).
Scan ( & f . ReviewCount , & f . ReviewRunning , & f . ReviewFailed , & f . ReviewFindings )
if err != nil {
return err
}
f . Participants , err = participants ( ctx , tx , taskID )
if err != nil {
return err
}
if reflect . DeepEqual ( f , t . MergeFacts ) {
return nil
}
_ , err = tx . ExecContext ( ctx , `
UPDATE tasks SET unmerged_commits = ?, unmerged_files = ?, unreviewed_changes = ?, review_snapshot = ?, review_count = ?, review_running = ?, review_failed = ?, review_findings = ?
WHERE id = ?
` , f . UnmergedCommits , f . UnmergedFiles , f . UnreviewedChanges , f . ReviewSnapshot , f . ReviewCount , f . ReviewRunning , f . ReviewFailed , f . ReviewFindings , taskID )
if err != nil {
return err
}
_ , err = tx . ExecContext ( ctx , `DELETE FROM task_participants WHERE task_id = ?` , taskID )
if err != nil {
return err
}
for i , p := range f . Participants {
_ , err = tx . ExecContext ( ctx , `INSERT INTO task_participants (task_id, position, login, display_name, git_name, git_email) VALUES (?, ?, ?, ?, ?, ?)` ,
taskID , i , p . Login , p . DisplayName , p . GitName , p . GitEmail )
if err != nil {
return err
}
}
return emit ( api . Event { Kind : "task.merge_facts" , ProjectId : & t . ProjectId , TaskId : & taskID , Actor : "byre" , Payload : ToPayload ( f )})
})
}