From 627f9eeeff96f60ab45cbe1e4afa89721d544a1a Mon Sep 17 00:00:00 2001 From: PJ Date: Tue, 18 Aug 2026 00:17:10 +0530 Subject: [PATCH] fix(implementation-sweep): name every missing required flag, in flag order --- .../implementation-sweep/main.go | 26 +++++++++++++----- .../implementation-sweep/main_test.go | 27 +++++++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/cmd/internal-tools/implementation-sweep/main.go b/cmd/internal-tools/implementation-sweep/main.go index 7cf2502..e1d48f6 100644 --- a/cmd/internal-tools/implementation-sweep/main.go +++ b/cmd/internal-tools/implementation-sweep/main.go @@ -155,16 +155,28 @@ func parseArguments(arguments []string, stderr io.Writer) (config, error) { } configuration.extraArguments = flagSet.Args() - for name, value := range map[string]string{ - "--implementations": configuration.implementationsDirectory, - "--spec": configuration.specPath, - "--seeds": seedSpecification, - "--output": configuration.outputDirectory, + // Every missing flag is named together, in flag order: stopping at the + // first turns one rerun into one rerun per missing flag. + var missing []error + for _, required := range []struct { + name string + value string + }{ + {"--implementations", configuration.implementationsDirectory}, + {"--spec", configuration.specPath}, + {"--seeds", seedSpecification}, + {"--output", configuration.outputDirectory}, } { - if value == "" { - return config{}, fmt.Errorf("%s is required", name) + if required.value == "" { + missing = append( + missing, + fmt.Errorf("%s is required", required.name), + ) } } + if err := errors.Join(missing...); err != nil { + return config{}, err + } if configuration.maxSteps <= 0 { return config{}, fmt.Errorf( "--max-steps must be positive: every implementation needs the same step budget", diff --git a/cmd/internal-tools/implementation-sweep/main_test.go b/cmd/internal-tools/implementation-sweep/main_test.go index f9d4d6d..1e5836e 100644 --- a/cmd/internal-tools/implementation-sweep/main_test.go +++ b/cmd/internal-tools/implementation-sweep/main_test.go @@ -143,6 +143,33 @@ func TestParseArguments_Rejections(t *testing.T) { } } +// Three flags missing is one rerun, not three: the operator is told about all +// of them at once, in flag order, whatever order the check happened to walk. +func TestParseArguments_NamesEveryMissingRequiredFlagInFlagOrder(t *testing.T) { + _, err := parseArguments( + []string{"--spec", "s", "--max-steps", "10"}, + io.Discard, + ) + if err == nil { + t.Fatal("got no error, want every missing flag named") + } + message := err.Error() + previous := -1 + for _, name := range []string{"--implementations", "--seeds", "--output"} { + at := strings.Index(message, name) + if at < 0 { + t.Fatalf("got %q, want %s named", message, name) + } + if at < previous { + t.Errorf("got %q, want the flags named in flag order", message) + } + previous = at + } + if strings.Contains(message, "--spec") { + t.Errorf("got %q, want the supplied --spec left out", message) + } +} + // The seed reaches two independent things, the campaign's own seed and the // scaffold's failure stream, and a replay reproduces neither unless they carry // the same number.