← back to Cli Printing Press
fix(scorer): resolve binary at build/stage/bin/ before cliDir (#1150) (#1412)
2abbfec04826d9763877490df1612d433f2debce · 2026-05-16 03:32:14 -0500 · kberger111
* fix(scorer): resolve binary at build/stage/bin/ before cliDir (#1150)
The Sample Output Probe was looking for the printed CLI's binary at
<cliDir>/<name>(.exe), but the generator's --validate "build runnable
binary" gate emits the binary at <cliDir>/build/stage/bin/<name>(.exe).
On every printed CLI in the catalog the probe missed and reported
"is not executable" — a scorer-calibration false alarm, not a real
defect.
Expand liveCheckBinaryCandidatesForGOOS to try the canonical staged
path first, then fall back to the legacy cliDir layout. This subsumes
the original .exe-suffix scope of #1150 (already partially fixed via
platform.ExecutablePathForGOOS) and addresses piercekiltoff's wider
finding from the servicetitan-crm retro: it's the directory, not just
the suffix.
Resolution order:
1. <cliDir>/build/stage/bin/<name> canonical Unix
2. <cliDir>/build/stage/bin/<name>.exe canonical Windows
3. <cliDir>/<name> legacy fallback
4. <cliDir>/<name>.exe legacy Windows fallback
Adds TestLiveCheckBinaryCandidatesPreferBuildStageBin to lock the
order in.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(scorer): strengthen candidate ordering + add build/stage/bin integration test
Per Greptile review on #1412:
1. Ordering test now asserts all four cross-product positions:
stagedUnix < legacyUnix, stagedUnix < legacyWin,
stagedWin < legacyUnix, stagedWin < legacyWin.
Previously only stagedUnix < legacyUnix was checked, leaving
a silent window for reorder regressions on the Windows paths.
2. TestLiveCheck_FindsBinaryInBuildStageBin exercises the full
RunLiveCheck probe with the stub binary placed at
build/stage/bin/<name> — the canonical post-v4.5.1 layout.
Skipped on Windows (shell-script stubs not supported there,
matching the existing integration-test convention).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Ken Berger <kberger@megadatahs.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Files touched
M internal/pipeline/live_check.goM internal/pipeline/live_check_test.go
Diff
commit 2abbfec04826d9763877490df1612d433f2debce
Author: kberger111 <ksberger@gmail.com>
Date: Sat May 16 03:32:14 2026 -0500
fix(scorer): resolve binary at build/stage/bin/ before cliDir (#1150) (#1412)
* fix(scorer): resolve binary at build/stage/bin/ before cliDir (#1150)
The Sample Output Probe was looking for the printed CLI's binary at
<cliDir>/<name>(.exe), but the generator's --validate "build runnable
binary" gate emits the binary at <cliDir>/build/stage/bin/<name>(.exe).
On every printed CLI in the catalog the probe missed and reported
"is not executable" — a scorer-calibration false alarm, not a real
defect.
Expand liveCheckBinaryCandidatesForGOOS to try the canonical staged
path first, then fall back to the legacy cliDir layout. This subsumes
the original .exe-suffix scope of #1150 (already partially fixed via
platform.ExecutablePathForGOOS) and addresses piercekiltoff's wider
finding from the servicetitan-crm retro: it's the directory, not just
the suffix.
Resolution order:
1. <cliDir>/build/stage/bin/<name> canonical Unix
2. <cliDir>/build/stage/bin/<name>.exe canonical Windows
3. <cliDir>/<name> legacy fallback
4. <cliDir>/<name>.exe legacy Windows fallback
Adds TestLiveCheckBinaryCandidatesPreferBuildStageBin to lock the
order in.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(scorer): strengthen candidate ordering + add build/stage/bin integration test
Per Greptile review on #1412:
1. Ordering test now asserts all four cross-product positions:
stagedUnix < legacyUnix, stagedUnix < legacyWin,
stagedWin < legacyUnix, stagedWin < legacyWin.
Previously only stagedUnix < legacyUnix was checked, leaving
a silent window for reorder regressions on the Windows paths.
2. TestLiveCheck_FindsBinaryInBuildStageBin exercises the full
RunLiveCheck probe with the stub binary placed at
build/stage/bin/<name> — the canonical post-v4.5.1 layout.
Skipped on Windows (shell-script stubs not supported there,
matching the existing integration-test convention).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Ken Berger <kberger@megadatahs.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
---
internal/pipeline/live_check.go | 12 ++++++++-
internal/pipeline/live_check_test.go | 49 ++++++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+), 1 deletion(-)
diff --git a/internal/pipeline/live_check.go b/internal/pipeline/live_check.go
index 62300394..b26007b0 100644
--- a/internal/pipeline/live_check.go
+++ b/internal/pipeline/live_check.go
@@ -276,13 +276,23 @@ func liveCheckBinaryCandidatesForGOOS(cliDir, name, goos string) []string {
base := filepath.Base(cliDir)
names = []string{base + "-pp-cli", base}
}
- candidates := make([]string, 0, len(names)*2)
+ // Resolution order (per issue #1150):
+ // 1. <cliDir>/build/stage/bin/<name> canonical Unix
+ // 2. <cliDir>/build/stage/bin/<name>.exe canonical Windows
+ // 3. <cliDir>/<name> legacy fallback
+ // 4. <cliDir>/<name>.exe legacy Windows fallback
+ // The generator's --validate "build runnable binary" gate emits the
+ // binary under build/stage/bin/; older layouts left it at cliDir.
+ stagedDir := filepath.Join(cliDir, "build", "stage", "bin")
+ candidates := make([]string, 0, len(names)*4)
seen := map[string]struct{}{}
for _, candidate := range names {
if candidate == "" {
continue
}
for _, path := range []string{
+ filepath.Join(stagedDir, candidate),
+ platform.ExecutablePathForGOOS(filepath.Join(stagedDir, candidate), goos),
filepath.Join(cliDir, candidate),
platform.ExecutablePathForGOOS(filepath.Join(cliDir, candidate), goos),
} {
diff --git a/internal/pipeline/live_check_test.go b/internal/pipeline/live_check_test.go
index 081dc2c4..85169a8a 100644
--- a/internal/pipeline/live_check_test.go
+++ b/internal/pipeline/live_check_test.go
@@ -508,6 +508,55 @@ func TestLiveCheck_BinaryAutoDerivation(t *testing.T) {
require.Contains(t, result.Features[0].Example, "stub x matched")
}
+func TestLiveCheckBinaryCandidatesPreferBuildStageBin(t *testing.T) {
+ t.Parallel()
+
+ dir := filepath.Join("tmp", "sample-cli")
+ stagedUnix := filepath.Join(dir, "build", "stage", "bin", "sample-cli-pp-cli")
+ stagedWin := platform.ExecutablePathForGOOS(filepath.Join(dir, "build", "stage", "bin", "sample-cli-pp-cli"), "windows")
+ legacyUnix := filepath.Join(dir, "sample-cli-pp-cli")
+
+ cands := liveCheckBinaryCandidatesForGOOS(dir, "", "windows")
+ assert.Contains(t, cands, stagedUnix)
+ assert.Contains(t, cands, stagedWin)
+ assert.Contains(t, cands, legacyUnix)
+
+ // Canonical staged path must come before the legacy cliDir fallback.
+ stagedIdx := -1
+ legacyIdx := -1
+ for i, c := range cands {
+ if c == stagedUnix && stagedIdx == -1 {
+ stagedIdx = i
+ }
+ if c == legacyUnix && legacyIdx == -1 {
+ legacyIdx = i
+ }
+ }
+ assert.True(t, stagedIdx >= 0 && legacyIdx >= 0 && stagedIdx < legacyIdx,
+ "staged build/stage/bin path must be tried before cliDir legacy path, got order %v", cands)
+}
+
+func TestLiveCheck_FindsBinaryInBuildStageBin(t *testing.T) {
+ // Verify that RunLiveCheck finds and executes a binary placed at
+ // <cliDir>/build/stage/bin/<name> — the canonical layout written by the
+ // generator's --validate "build runnable binary" gate (post-v4.5.1).
+ if runtime.GOOS == "windows" {
+ t.Skip("shell-script stub not supported on Windows")
+ }
+ dir := t.TempDir()
+ stagedBinDir := filepath.Join(dir, "build", "stage", "bin")
+ require.NoError(t, os.MkdirAll(stagedBinDir, 0o755))
+ stub := filepath.Join(stagedBinDir, "stub")
+ require.NoError(t, os.WriteFile(stub, []byte("#!/bin/sh\necho '{\"data\":[{\"id\":\"1\"}]}'\n"), 0o755))
+ writeTestResearchJSON(t, dir, []NovelFeature{
+ {Name: "List items", Command: "items list", Example: "stub items list --json"},
+ })
+ result := RunLiveCheck(LiveCheckOptions{CLIDir: dir, BinaryName: "stub", Timeout: 5 * time.Second})
+ require.False(t, result.Unable, "check was Unable: %s", result.Reason)
+ require.Equal(t, 1, result.Checked())
+ assert.Equal(t, 1, result.Passed, "expected binary at build/stage/bin/ to be found and run")
+}
+
func TestLiveCheckBinaryCandidatesIncludeHostExecutableName(t *testing.T) {
t.Parallel()
← 793d5b27 fix(skills): surface novel-feature hand-code scope at Phase
·
back to Cli Printing Press
·
fix(skills): drop cli-skills mirror regen from publish flow f64e9913 →