[object Object]

← back to Cli Printing Press

refactor(cli): split structural verify runtime (#320)

7e733aa7715d7fe29405335883bb7e6740260397 · 2026-04-26 18:13:40 -0700 · Trevin Chow

Move spec-independent verify into its own runtime file now that the verify golden matrix covers structural PASS and FAIL behavior. Keep RunVerify dispatch and helper calls unchanged so the split stays behavior-preserving.

Files touched

Diff

commit 7e733aa7715d7fe29405335883bb7e6740260397
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Sun Apr 26 18:13:40 2026 -0700

    refactor(cli): split structural verify runtime (#320)
    
    Move spec-independent verify into its own runtime file now that the verify golden matrix covers structural PASS and FAIL behavior. Keep RunVerify dispatch and helper calls unchanged so the split stays behavior-preserving.
---
 internal/pipeline/runtime.go            | 99 ---------------------------------
 internal/pipeline/runtime_structural.go | 91 ++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 99 deletions(-)

diff --git a/internal/pipeline/runtime.go b/internal/pipeline/runtime.go
index 9801faab..fd13c759 100644
--- a/internal/pipeline/runtime.go
+++ b/internal/pipeline/runtime.go
@@ -216,105 +216,6 @@ func RunVerify(cfg VerifyConfig) (*VerifyReport, error) {
 	return report, nil
 }
 
-// runStructuralVerify runs spec-independent verification: build, --help,
-// --json validity, version, and exit code checks for every discovered command.
-func runStructuralVerify(cfg VerifyConfig) (*VerifyReport, error) {
-	if cfg.Threshold == 0 {
-		cfg.Threshold = 80
-	}
-	if err := artifacts.CleanupGeneratedCLI(cfg.Dir, artifacts.CleanupOptions{
-		RemoveValidationBinaries: true,
-		RemoveDogfoodBinaries:    true,
-		RemoveRecursiveCopies:    true,
-		RemoveFinderMetadata:     true,
-	}); err != nil {
-		return nil, fmt.Errorf("pre-verify cleanup: %w", err)
-	}
-
-	report := &VerifyReport{Mode: "structural"}
-
-	// 1. Build the CLI
-	binaryPath, err := buildCLI(cfg.Dir)
-	if err != nil {
-		return nil, fmt.Errorf("building CLI: %w", err)
-	}
-	report.Binary = binaryPath
-
-	// 2. Discover commands from --help output
-	commands := discoverCommands(cfg.Dir, binaryPath)
-
-	// 3. Test each command structurally
-	for _, cmd := range commands {
-		result := runStructuralCommandTests(binaryPath, cmd)
-		report.Results = append(report.Results, result)
-	}
-
-	// 4. Version command check
-	versionOK := runCLI(binaryPath, []string{"version"}, os.Environ(), 10*time.Second) == nil
-	if !versionOK {
-		versionOK = runCLI(binaryPath, []string{"--version"}, os.Environ(), 10*time.Second) == nil
-	}
-	report.DataPipeline = versionOK
-	if versionOK {
-		report.DataPipelineDetail = "PASS (version command)"
-	} else {
-		report.DataPipelineDetail = "FAIL (version command)"
-	}
-	report.Freshness = runFreshnessContractTest(cfg.Dir)
-
-	finalizeVerifyReport(report, cfg.Threshold, false)
-
-	return report, nil
-}
-
-// runStructuralCommandTests tests a command without API access: --help output,
-// --json flag acceptance (doesn't crash), and exit code correctness.
-func runStructuralCommandTests(binary string, cmd discoveredCommand) CommandResult {
-	result := CommandResult{
-		Command: cmd.Name,
-		Kind:    "structural",
-	}
-
-	// Test 1: --help produces output and exits 0
-	result.Help = runCLI(binary, []string{cmd.Name, "--help"}, os.Environ(), 10*time.Second) == nil
-
-	// Test 2: --help --json doesn't crash (validates flag registration)
-	result.DryRun = runCLI(binary, []string{cmd.Name, "--help", "--json"}, os.Environ(), 10*time.Second) == nil
-
-	// Test 3: command with no args exits non-zero if it requires args/flags
-	// (validates that required flags are enforced). Skip commands that work
-	// without args (doctor, version, auth, completion, api).
-	switch cmd.Name {
-	case "doctor", "version", "auth", "completion", "api", "help":
-		result.Execute = true // these work without args
-	default:
-		// Running with just --json and no other args. If the command requires
-		// flags/args, it should exit non-zero with an error message.
-		// If it works without args, that's fine too.
-		// Either way, we're validating it doesn't crash/panic.
-		err := runCLI(binary, []string{cmd.Name, "--json"}, os.Environ(), 10*time.Second)
-		// Both outcomes are acceptable for structural verification: the
-		// command either ran successfully or exited with a proper error.
-		// A panic or timeout would still fail via runCLI.
-		result.Execute = true
-		_ = err
-	}
-
-	score := 0
-	if result.Help {
-		score++
-	}
-	if result.DryRun {
-		score++
-	}
-	if result.Execute {
-		score++
-	}
-	result.Score = score
-
-	return result
-}
-
 // buildCLI compiles the generated CLI and returns the binary path.
 func buildCLI(dir string) (string, error) {
 	name := filepath.Base(dir)
diff --git a/internal/pipeline/runtime_structural.go b/internal/pipeline/runtime_structural.go
new file mode 100644
index 00000000..a8ae49bc
--- /dev/null
+++ b/internal/pipeline/runtime_structural.go
@@ -0,0 +1,91 @@
+package pipeline
+
+import (
+	"fmt"
+	"os"
+	"time"
+
+	"github.com/mvanhorn/cli-printing-press/v2/internal/artifacts"
+)
+
+// runStructuralVerify runs spec-independent verification: build, --help,
+// --json validity, version, and exit code checks for every discovered command.
+func runStructuralVerify(cfg VerifyConfig) (*VerifyReport, error) {
+	if cfg.Threshold == 0 {
+		cfg.Threshold = 80
+	}
+	if err := artifacts.CleanupGeneratedCLI(cfg.Dir, artifacts.CleanupOptions{
+		RemoveValidationBinaries: true,
+		RemoveDogfoodBinaries:    true,
+		RemoveRecursiveCopies:    true,
+		RemoveFinderMetadata:     true,
+	}); err != nil {
+		return nil, fmt.Errorf("pre-verify cleanup: %w", err)
+	}
+
+	report := &VerifyReport{Mode: "structural"}
+
+	binaryPath, err := buildCLI(cfg.Dir)
+	if err != nil {
+		return nil, fmt.Errorf("building CLI: %w", err)
+	}
+	report.Binary = binaryPath
+
+	commands := discoverCommands(cfg.Dir, binaryPath)
+
+	for _, cmd := range commands {
+		result := runStructuralCommandTests(binaryPath, cmd)
+		report.Results = append(report.Results, result)
+	}
+
+	versionOK := runCLI(binaryPath, []string{"version"}, os.Environ(), 10*time.Second) == nil
+	if !versionOK {
+		versionOK = runCLI(binaryPath, []string{"--version"}, os.Environ(), 10*time.Second) == nil
+	}
+	report.DataPipeline = versionOK
+	if versionOK {
+		report.DataPipelineDetail = "PASS (version command)"
+	} else {
+		report.DataPipelineDetail = "FAIL (version command)"
+	}
+	report.Freshness = runFreshnessContractTest(cfg.Dir)
+
+	finalizeVerifyReport(report, cfg.Threshold, false)
+
+	return report, nil
+}
+
+// runStructuralCommandTests tests a command without API access: --help output,
+// --json flag acceptance (doesn't crash), and exit code correctness.
+func runStructuralCommandTests(binary string, cmd discoveredCommand) CommandResult {
+	result := CommandResult{
+		Command: cmd.Name,
+		Kind:    "structural",
+	}
+
+	result.Help = runCLI(binary, []string{cmd.Name, "--help"}, os.Environ(), 10*time.Second) == nil
+	result.DryRun = runCLI(binary, []string{cmd.Name, "--help", "--json"}, os.Environ(), 10*time.Second) == nil
+
+	switch cmd.Name {
+	case "doctor", "version", "auth", "completion", "api", "help":
+		result.Execute = true // these work without args
+	default:
+		err := runCLI(binary, []string{cmd.Name, "--json"}, os.Environ(), 10*time.Second)
+		result.Execute = true
+		_ = err
+	}
+
+	score := 0
+	if result.Help {
+		score++
+	}
+	if result.DryRun {
+		score++
+	}
+	if result.Execute {
+		score++
+	}
+	result.Score = score
+
+	return result
+}

← ee923f84 refactor(cli): share verify report finalization (#319)  ·  back to Cli Printing Press  ·  refactor(cli): split verify exec helpers (#321) 18cf50f1 →