From 3576455960ee88cefa43cad0bdfd1458549569b9 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 30 Jan 2026 20:25:26 +0100 Subject: pkg/aflow/flow/patching: use recent commit subjects Give LLM the recent commit subjects when it generates description, so that it can use the same style. Add infrastrcuture to write end-to-end action tests to test it. --- pkg/aflow/flow/patching/actions.go | 32 ++++++++++++++++++ pkg/aflow/flow/patching/actions_test.go | 59 +++++++++++++++++++++++++++++++++ pkg/aflow/flow/patching/patching.go | 7 ++++ 3 files changed, 98 insertions(+) create mode 100644 pkg/aflow/flow/patching/actions_test.go (limited to 'pkg/aflow/flow/patching') diff --git a/pkg/aflow/flow/patching/actions.go b/pkg/aflow/flow/patching/actions.go index 2c98e9306..d42c24fa1 100644 --- a/pkg/aflow/flow/patching/actions.go +++ b/pkg/aflow/flow/patching/actions.go @@ -4,6 +4,8 @@ package patching import ( + "errors" + "fmt" "os/exec" "path/filepath" "strings" @@ -108,3 +110,33 @@ func maintainers(ctx *aflow.Context, args maintainersArgs) (maintainersResult, e } return res, nil } + +var getRecentCommits = aflow.NewFuncAction("get-recent-commits", recentCommits) + +type recentCommitsArgs struct { + KernelSrc string + KernelCommit string + PatchDiff string +} + +type recentCommitsResult struct { + RecentCommits string +} + +func recentCommits(ctx *aflow.Context, args recentCommitsArgs) (recentCommitsResult, error) { + var res recentCommitsResult + var files []string + for _, file := range vcs.ParseGitDiff([]byte(args.PatchDiff)) { + files = append(files, file.Name) + } + if len(files) == 0 { + return res, aflow.FlowError(errors.New("patch diff does not contain any modified files")) + } + gitArgs := append([]string{"log", "--format=%s", "--no-merges", "-n", "20", args.KernelCommit}, files...) + output, err := osutil.RunCmd(10*time.Minute, args.KernelSrc, "git", gitArgs...) + if err != nil { + return res, aflow.FlowError(fmt.Errorf("%w\n%s", err, output)) + } + res.RecentCommits = string(output) + return res, nil +} diff --git a/pkg/aflow/flow/patching/actions_test.go b/pkg/aflow/flow/patching/actions_test.go new file mode 100644 index 000000000..c6f3011e7 --- /dev/null +++ b/pkg/aflow/flow/patching/actions_test.go @@ -0,0 +1,59 @@ +// Copyright 2026 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package patching + +import ( + "os" + "path/filepath" + "testing" + + "github.com/google/syzkaller/pkg/aflow" +) + +func TestRecentCommits(t *testing.T) { + // To avoid creating a fake git repo, we use the syzkaller repo itself. + // On CI we have a shallow git checkout that does not have the commit. + if os.Getenv("CI") != "" { + t.Skip("skipping on CI because of shallow git checkout") + } + aflow.TestAction(t, getRecentCommits, recentCommitsArgs{ + KernelSrc: filepath.FromSlash("../../../.."), + KernelCommit: "e01a0ca6c12c9851ea7090f13879255ef82291e7", + PatchDiff: ` +diff --git a/dashboard/app/ai.go b/dashboard/app/ai.go +index d4539113c..1d7401e61 100644 +--- a/dashboard/app/ai.go ++++ b/dashboard/app/ai.go +@@ -1,2 +1,2 @@ +-// Copyright 2025 syzkaller project authors. All rights reserved. ++// Copyright 2026 syzkaller project authors. All rights reserved. + // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. +diff --git a/syz-cluster/pkg/fuzzconfig/generate.go b/syz-cluster/pkg/fuzzconfig/generate.go +index fa7d082e6..74ec57b49 100644 +--- a/syz-cluster/pkg/fuzzconfig/generate.go ++++ b/syz-cluster/pkg/fuzzconfig/generate.go +@@ -1,2 +1,2 @@ +-// Copyright 2025 syzkaller project authors. All rights reserved. ++// Copyright 2026 syzkaller project authors. All rights reserved. + // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. +`, + }, recentCommitsResult{RecentCommits: `dashboard: run patching ai jobs on custom base commits +dashboard/app: upload AI-generated patches to gerrit +dashboard: journal user actions on the ai dashboard +pkg/aflow/trajectory: add token usage +dashboard/app: add AI job running status +dashboard: filter AI jobs by workflows +syz-cluster: disable some trace calls for non-bpf targets +pkg/aflow: make LLM model per-agent rather than per-flow +dashboard/app: show crash report on AI job page +dashboard/app: improve AI UI +pkg/aflow: allow to specify model per-flow +dashboard/app: add race harmfullness label +dashboard/app: add manual AI job triage +pkg/aflow/flow/assessment: add UAF moderation workflow +dashboard/app: add support for AI workflows +syz-cluster: rewrite fuzz config generation +`, + }, "") +} diff --git a/pkg/aflow/flow/patching/patching.go b/pkg/aflow/flow/patching/patching.go index 1906204ec..f8ee75a31 100644 --- a/pkg/aflow/flow/patching/patching.go +++ b/pkg/aflow/flow/patching/patching.go @@ -75,6 +75,7 @@ func createPatchingFlow(name string, summaryWindow int) *aflow.Flow { MaxIterations: 10, }, getMaintainers, + getRecentCommits, &aflow.LLMAgent{ Name: "description-generator", Model: aflow.BestExpensiveModel, @@ -222,6 +223,12 @@ Additional description of the patch: {{.PatchExplanation}} +Here are summaries of recent commits that touched the same files. +Format the summary line consistently with these, look how prefixes +are specified, letter capitalization, style, etc. + +{{.RecentCommits}} + {{if titleIsWarning .BugTitle}} If the patch removes the WARN_ON macro, refer to the fact that WARN_ON must not be used for conditions that can legitimately happen, and that pr_err -- cgit mrf-deployment