From f30b546c003d5a3c45bf7984d5529308b1656b47 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:30:49 +0000 Subject: [PATCH 1/4] Initial plan From 4a6399c3b0c98ebb65064400589a42e8f70d0883 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:45:25 +0000 Subject: [PATCH 2/4] Optimize safe-output sample field iteration in validation path Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/samples_replay.go | 4 +--- pkg/workflow/samples_validation.go | 12 +++++++++++- pkg/workflow/samples_validation_test.go | 12 ++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pkg/workflow/samples_replay.go b/pkg/workflow/samples_replay.go index 76bf781de66..fd7a3b62131 100644 --- a/pkg/workflow/samples_replay.go +++ b/pkg/workflow/samples_replay.go @@ -4,8 +4,6 @@ import ( "encoding/json" "fmt" "strings" - - "github.com/github/gh-aw/pkg/sliceutil" ) // SampleEntry is the per-call payload consumed by apply_samples.cjs. @@ -30,7 +28,7 @@ func collectSampleEntries(config *SafeOutputsConfig) []SampleEntry { return nil } - fieldNames := sliceutil.SortedKeys(safeOutputFieldMapping) + fieldNames := getSortedSafeOutputFieldNames() var entries []SampleEntry for _, fieldName := range fieldNames { diff --git a/pkg/workflow/samples_validation.go b/pkg/workflow/samples_validation.go index a6ebb0008a2..9462b6c4a1d 100644 --- a/pkg/workflow/samples_validation.go +++ b/pkg/workflow/samples_validation.go @@ -61,6 +61,9 @@ var ( compiledToolSchemasOnce sync.Once compiledToolSchemas map[string]toolSchemaEntry compiledToolSchemasErr error + + sortedSafeOutputFieldNamesOnce sync.Once + sortedSafeOutputFieldNames []string ) func getCompiledToolSchemas() (map[string]toolSchemaEntry, error) { @@ -97,6 +100,13 @@ func getCompiledToolSchemas() (map[string]toolSchemaEntry, error) { return compiledToolSchemas, compiledToolSchemasErr } +func getSortedSafeOutputFieldNames() []string { + sortedSafeOutputFieldNamesOnce.Do(func() { + sortedSafeOutputFieldNames = sliceutil.SortedKeys(safeOutputFieldMapping) + }) + return sortedSafeOutputFieldNames +} + // validateSafeOutputsSamples validates every `samples` entry on every // enabled safe-output handler against the corresponding MCP tool's inputSchema. // Sample sidecar fields (e.g. `patch`) are stripped before validation. Returns @@ -107,7 +117,7 @@ func validateSafeOutputsSamples(config *SafeOutputsConfig) error { return nil } - fieldNames := sliceutil.SortedKeys(safeOutputFieldMapping) + fieldNames := getSortedSafeOutputFieldNames() samplesValidationLog.Printf("Validating safe-outputs samples across %d candidate fields", len(fieldNames)) for _, fieldName := range fieldNames { diff --git a/pkg/workflow/samples_validation_test.go b/pkg/workflow/samples_validation_test.go index a69ac2a6d28..1e09d990652 100644 --- a/pkg/workflow/samples_validation_test.go +++ b/pkg/workflow/samples_validation_test.go @@ -4,10 +4,22 @@ import ( "strings" "testing" + "github.com/github/gh-aw/pkg/sliceutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestGetSortedSafeOutputFieldNames_MatchesSortedKeys(t *testing.T) { + t.Parallel() + + expected := sliceutil.SortedKeys(safeOutputFieldMapping) + first := getSortedSafeOutputFieldNames() + second := getSortedSafeOutputFieldNames() + + assert.Equal(t, expected, first) + assert.Equal(t, expected, second) +} + // TestValidateSafeOutputsSamples_Valid covers the happy path for the // strict schema validation of samples entries. We use create_issue (no // sidecars, just title/body) and create_pull_request (with the `patch` sidecar From 69b66831690b2d14b37f0a7b7fa035f16caf12c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:48:55 +0000 Subject: [PATCH 3/4] Strengthen cached field-name test for safe-output samples Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/samples_validation_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/workflow/samples_validation_test.go b/pkg/workflow/samples_validation_test.go index 1e09d990652..ad175fd3bc5 100644 --- a/pkg/workflow/samples_validation_test.go +++ b/pkg/workflow/samples_validation_test.go @@ -18,6 +18,8 @@ func TestGetSortedSafeOutputFieldNames_MatchesSortedKeys(t *testing.T) { assert.Equal(t, expected, first) assert.Equal(t, expected, second) + require.NotEmpty(t, first) + assert.Equal(t, &first[0], &second[0], "expected cached field names to reuse the same backing slice") } // TestValidateSafeOutputsSamples_Valid covers the happy path for the From 81dfd34df15d2f54b4257d6842b2b9d146dd1cbf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:53:20 +0000 Subject: [PATCH 4/4] Harden cached field-order test for safe-output samples Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/samples_validation_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/workflow/samples_validation_test.go b/pkg/workflow/samples_validation_test.go index ad175fd3bc5..546e09f9d8f 100644 --- a/pkg/workflow/samples_validation_test.go +++ b/pkg/workflow/samples_validation_test.go @@ -18,8 +18,9 @@ func TestGetSortedSafeOutputFieldNames_MatchesSortedKeys(t *testing.T) { assert.Equal(t, expected, first) assert.Equal(t, expected, second) - require.NotEmpty(t, first) - assert.Equal(t, &first[0], &second[0], "expected cached field names to reuse the same backing slice") + if assert.NotEmpty(t, first) && assert.NotEmpty(t, second) { + assert.Equal(t, &first[0], &second[0], "expected cached field names to reuse the same backing slice") + } } // TestValidateSafeOutputsSamples_Valid covers the happy path for the