diff --git a/completion_shell_test.go b/completion_shell_test.go index 99c5579..215c472 100644 --- a/completion_shell_test.go +++ b/completion_shell_test.go @@ -32,9 +32,9 @@ func TestCompletionConsumerInShells(t *testing.T) { directory bool }{ {"canonical command", "probe ru", []string{"run"}, false}, - {"entered alias", "probe shortcut --format=ya", []string{"shortcut", "--format=yaml"}, false}, - {"choice", "probe run --format=te", []string{"run", "--format=text"}, false}, - {"no file fallback", `probe run --format=./partial\ f`, []string{"run", "--format=./partial f"}, false}, + {"entered alias", "probe shortcut --choice=ya", []string{"shortcut", "--choice=yaml"}, false}, + {"choice", "probe run --choice=te", []string{"run", "--choice=text"}, false}, + {"no file fallback", `probe run --choice=./partial\ f`, []string{"run", "--choice=./partial f"}, false}, {"separated file", `probe run --config ./partial\ f`, []string{"run", "--config", "./partial file"}, false}, {"attached file", `probe run --config=./partial\ f`, []string{"run", "--config=./partial file"}, false}, {"short attached file", `probe run -c./partial\ f`, []string{"run", "-c./partial file"}, false}, @@ -42,7 +42,7 @@ func TestCompletionConsumerInShells(t *testing.T) { {"separated directory", "probe run --directory ./partial", []string{"run", "--directory", "./partial dir"}, true}, {"attached directory", "probe run --directory=./partial", []string{"run", "--directory=./partial dir"}, true}, {"empty operand", "probe run ", []string{"run", "alpha"}, false}, - {"leading dash", "probe run --for", []string{"run", "--format"}, false}, + {"leading dash", "probe run --cho", []string{"run", "--choice"}, false}, } for _, shell := range []string{"bash", "zsh", "fish"} { generator := exec.Command(app, "completion", shell) @@ -82,7 +82,7 @@ func TestCompletionConsumerInShells(t *testing.T) { }) } t.Run(shell+"/descriptions", func(t *testing.T) { - terminal := exec.Command("python3", "internal/cobra/testdata/completion_terminal.py", shell, scriptPath, "probe run --format=") + terminal := exec.Command("python3", "internal/cobra/testdata/completion_terminal.py", shell, scriptPath, "probe run --choice=") terminal.Env = append(os.Environ(), "PATH="+dir+string(os.PathListSeparator)+os.Getenv("PATH"), "HOME="+dir, "TERM=xterm", "PROBE_LOG="+filepath.Join(dir, "log"), "PROBE_DIR="+dir, "PROBE_LIST=1") output, err := terminal.CombinedOutput() if err != nil || !strings.Contains(string(output), "Readable text") || !strings.Contains(string(output), "YAML output") { diff --git a/docs/roadmap/audit/catalog.md b/docs/roadmap/audit/catalog.md index 42090f0..535a341 100644 --- a/docs/roadmap/audit/catalog.md +++ b/docs/roadmap/audit/catalog.md @@ -1,6 +1,6 @@ # Consolidated audit catalog -Status: **22 distinct open findings: 22 P3**, consolidated from 30 +Status: **20 distinct open findings: 20 P3**, consolidated from 30 numbered entries in seven audit documents. Reviewed audit state: `db732e3a1de55c844f77870e1c6db683c2685d5d`. @@ -142,6 +142,10 @@ Also noted in the [documentation cross-references](13-documentation.md#previousl ## F06 — P3: The optional interactive completion consumer fails before shell validation +**Complete.** The renamed application-owned choice flag permits script +generation and the Bash interactive consumer and backend checks pass. Zsh and +Fish interactive checks remain unavailable because those shells are not installed. + The completion consumer fixture declares the reserved `format` flag, so App construction fails before it can generate scripts or exercise interactive completion. Default test suites skip this optional path. @@ -169,6 +173,9 @@ through a reachable handle, not an unreachable heap leak. ## F08 — P3: The core example overwrites its optional argument default +**Complete.** The example assigns a scenario only when supplied; bare and +explicit demo invocations succeed and an invalid scenario returns usage status 2. + The example's scenario setter replaces the factory's `"demo"` default with an unsupplied empty value. The ordinary `report` invocation consequently fails with a usage error. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 5237757..f708247 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,6 +1,6 @@ # Audit remediation implementation plan -Status: in progress; Stages 1–5 complete. +Status: in progress; Stages 1–6 complete. Baseline: `d492c7747175c01a53261206c2b4356e53453621`. Before this sprint, intervening changes recorded decisions and retired the completed older @@ -250,6 +250,13 @@ presentation failures remain separately observable. ## Stage 6 — Repair the maintained consumers +**Complete.** The fixture generates Bash, Zsh and Fish scripts; the available +interactive Bash backend and consumer checks passed. The full opt-in commands +reported failures for Zsh and Fish because those shells are not installed; +their interactive behavior remains unverified. The core example's bare and +explicit `demo` invocations succeeded, and an invalid scenario returned usage +status 2. Checks used Go 1.27.0 with offline, read-only module resolution. + Findings: F06 and F08. Sources: `testdata/completion/main.go`, `completion_shell_test.go`, and `examples/core/main.go`. diff --git a/examples/core/main.go b/examples/core/main.go index d95a2ea..f46e26c 100644 --- a/examples/core/main.go +++ b/examples/core/main.go @@ -74,7 +74,11 @@ func newApp() (*commandkit.App, error) { commandkit.ValueArgument(commandkit.ArgumentMeta{Name: "scenario", Usage: "demo, recover, warn, warn-error, fail, or partial.", Completion: commandkit.CompleteChoices( commandkit.CompletionChoice{Value: "demo"}, commandkit.CompletionChoice{Value: "recover"}, commandkit.CompletionChoice{Value: "warn"}, commandkit.CompletionChoice{Value: "warn-error"}, commandkit.CompletionChoice{Value: "fail"}, commandkit.CompletionChoice{Value: "partial"})}, false, func(value string) (string, error) { return value, nil }, - func(input *reportInput, value commandkit.Supplied[string]) { input.Scenario = value.Value }), + func(input *reportInput, value commandkit.Supplied[string]) { + if value.Set { + input.Scenario = value.Value + } + }), }, CheckInput: func(input reportInput) error { switch input.Scenario { diff --git a/testdata/completion/main.go b/testdata/completion/main.go index 702edec..42d6db1 100644 --- a/testdata/completion/main.go +++ b/testdata/completion/main.go @@ -29,7 +29,7 @@ func main() { Name: "run", Summary: "Run a fixture command.", Flags: []commandkit.Flag[struct{}]{ commandkit.BoolFlag(commandkit.FlagMeta{Name: "switch", Short: "s"}, false, func(*struct{}, commandkit.Supplied[bool]) {}), - flag(commandkit.FlagMeta{Name: "format", Usage: "Report format.", Completion: commandkit.CompleteChoices(commandkit.CompletionChoice{Value: "text", Description: "Readable text"}, commandkit.CompletionChoice{Value: "yaml", Description: "YAML output"})}), + flag(commandkit.FlagMeta{Name: "choice", Usage: "Fixture choice.", Completion: commandkit.CompleteChoices(commandkit.CompletionChoice{Value: "text", Description: "Readable text"}, commandkit.CompletionChoice{Value: "yaml", Description: "YAML output"})}), flag(commandkit.FlagMeta{Name: "config", Short: "c", Usage: "Configuration path.", Completion: commandkit.CompleteFiles()}), flag(commandkit.FlagMeta{Name: "directory", Short: "d", Usage: "Directory path.", Completion: commandkit.CompleteDirectories()}), },