From ae7dc18c2a1cb76ec4d3f158ba5f503ed91ac1b6 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 29 Jan 2026 09:32:25 +0100 Subject: dashboard/app: upload AI-generated patches to gerrit --- dashboard/app/ai.go | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/dashboard/app/ai.go b/dashboard/app/ai.go index bd01b0ea6..970d3f588 100644 --- a/dashboard/app/ai.go +++ b/dashboard/app/ai.go @@ -17,10 +17,13 @@ import ( "github.com/google/syzkaller/dashboard/app/aidb" "github.com/google/syzkaller/dashboard/dashapi" "github.com/google/syzkaller/pkg/aflow/ai" + "github.com/google/syzkaller/pkg/email" + "github.com/google/syzkaller/pkg/gerrit" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/report/crash" "github.com/google/syzkaller/pkg/vcs" db "google.golang.org/appengine/v2/datastore" + "google.golang.org/appengine/v2/log" ) const AIAccessLevel = AccessUser @@ -389,8 +392,15 @@ func apiAIJobDone(ctx context.Context, req *dashapi.AIJobDoneReq) (any, error) { if len(req.Results) != 0 { job.Results = spanner.NullJSON{Value: req.Results, Valid: true} } - err = aiJobUpdate(ctx, job) - return nil, err + if err = aiJobUpdate(ctx, job); err != nil { + return nil, err + } + if job.Type == ai.WorkflowPatching && job.BugID.Valid && job.Finished.Valid && job.Error == "" { + if err := createGerritChange(ctx, job); err != nil { + log.Errorf(ctx, "failed to create gerrit change for job %v: %v", job.ID, err) + } + } + return nil, nil } func aiJobUpdate(ctx context.Context, job *aidb.Job) error { @@ -665,6 +675,28 @@ func workflowsForBug(bug *Bug, manual bool) map[ai.WorkflowType]bool { return workflows } +func createGerritChange(ctx context.Context, job *aidb.Job) error { + res, err := castJobResults[ai.PatchingOutputs](job) + if err != nil { + return err + } + // TODO: add Reported-by tag for the syzbot bug, or a link to lore report. + // Add Fixes tag if we have cause bisection, but we need to verify it with LLMs + // somehow since lots of them are wrong. + // Probably shouldn't cc stable for all patches (e.g. removing a WARNING)? + res.Recipients = append(res.Recipients, ai.Recipient{Email: "stable@vger.kernel.org"}) + // TODO: move these constants to config. + const author = "syzbot@kernel.org" + description := email.FormatPatchDescription(res.PatchDescription, []string{author}, res.Recipients) + changeID, link, err := gerrit.CreateChange(ctx, res.KernelRepo, res.KernelBranch, + res.KernelCommit, description, res.PatchDiff) + if err != nil { + return err + } + log.Infof(ctx, "created gerrit change %v for job %v: %v", changeID, job.ID, link) + return nil +} + const ( aiCorrectnessCorrect = "✅" aiCorrectnessIncorrect = "❌" -- cgit mrf-deployment