From 5ab01f383a944d2a34e5b0404b546b7e48b25c44 Mon Sep 17 00:00:00 2001 From: furiosa Date: Mon, 26 Jan 2026 11:10:35 -0800 Subject: [PATCH] fix(version): suppress staleness warning for fork builds When the gt binary is built from a local fork (e.g., ~/src/gastown), the staleness check would incorrectly warn about being stale because it compared against the rig's repo HEAD. This confused agents. The fix detects fork builds by checking if the binary's embedded commit exists in the target repo. If not, the binary was built from a different repo and we report "fork build" instead of "stale". Changes: - Add IsForkBuild field to StaleBinaryInfo - Add commitExistsInRepo helper to detect fork scenarios - Update gt stale command to show "Binary built from fork" status - Update doctor check to report fork builds as OK Co-Authored-By: Claude Opus 4.5 --- internal/cmd/stale.go | 11 +++++++++-- internal/doctor/stale_binary_check.go | 10 ++++++++++ internal/version/stale.go | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/internal/cmd/stale.go b/internal/cmd/stale.go index 068024ff..1a019652 100644 --- a/internal/cmd/stale.go +++ b/internal/cmd/stale.go @@ -43,6 +43,7 @@ func init() { // StaleOutput represents the JSON output structure. type StaleOutput struct { Stale bool `json:"stale"` + ForkBuild bool `json:"fork_build,omitempty"` BinaryCommit string `json:"binary_commit"` RepoCommit string `json:"repo_commit"` CommitsBehind int `json:"commits_behind,omitempty"` @@ -77,8 +78,9 @@ func runStale(cmd *cobra.Command, args []string) error { } // Quiet mode: just exit with appropriate code + // Fork builds are treated as "fresh" (exit 1) since they're intentional if staleQuiet { - if info.IsStale { + if info.IsStale && !info.IsForkBuild { os.Exit(0) } os.Exit(1) @@ -87,6 +89,7 @@ func runStale(cmd *cobra.Command, args []string) error { // Build output output := StaleOutput{ Stale: info.IsStale, + ForkBuild: info.IsForkBuild, BinaryCommit: info.BinaryCommit, RepoCommit: info.RepoCommit, CommitsBehind: info.CommitsBehind, @@ -106,7 +109,11 @@ func outputStaleJSON(output StaleOutput) error { } func outputStaleText(output StaleOutput) error { - if output.Stale { + if output.ForkBuild { + fmt.Printf("%s Binary built from fork\n", style.Success.Render("✓")) + fmt.Printf(" Commit: %s\n", version.ShortCommit(output.BinaryCommit)) + fmt.Printf(" %s\n", style.Dim.Render("(commit not in rig repo - likely built from local fork)")) + } else if output.Stale { fmt.Printf("%s Binary is stale\n", style.Warning.Render("⚠")) fmt.Printf(" Binary: %s\n", version.ShortCommit(output.BinaryCommit)) fmt.Printf(" Repo: %s\n", version.ShortCommit(output.RepoCommit)) diff --git a/internal/doctor/stale_binary_check.go b/internal/doctor/stale_binary_check.go index 60a64c75..cf7ed0f9 100644 --- a/internal/doctor/stale_binary_check.go +++ b/internal/doctor/stale_binary_check.go @@ -46,6 +46,16 @@ func (c *StaleBinaryCheck) Run(ctx *CheckContext) *CheckResult { } } + // Fork builds are fine - the binary was built from a different repo + if info.IsForkBuild { + return &CheckResult{ + Name: c.Name(), + Status: StatusOK, + Message: fmt.Sprintf("Binary built from fork (%s)", version.ShortCommit(info.BinaryCommit)), + Details: []string{"Binary commit not in rig repo - likely built from local fork"}, + } + } + if info.IsStale { msg := fmt.Sprintf("Binary is stale (built from %s, repo at %s)", version.ShortCommit(info.BinaryCommit), version.ShortCommit(info.RepoCommit)) diff --git a/internal/version/stale.go b/internal/version/stale.go index 2c62f188..bc10c390 100644 --- a/internal/version/stale.go +++ b/internal/version/stale.go @@ -21,6 +21,7 @@ var ( // StaleBinaryInfo contains information about binary staleness. type StaleBinaryInfo struct { IsStale bool // True if binary commit doesn't match repo HEAD + IsForkBuild bool // True if binary was built from a different repo (fork) BinaryCommit string // Commit hash the binary was built from RepoCommit string // Current repo HEAD commit CommitsBehind int // Number of commits binary is behind (0 if unknown) @@ -66,6 +67,15 @@ func commitsMatch(a, b string) bool { return strings.HasPrefix(a, b[:minLen]) || strings.HasPrefix(b, a[:minLen]) } +// commitExistsInRepo checks if a commit hash exists in the given repository. +// This is used to detect fork builds - if the binary's commit doesn't exist +// in the target repo, the binary was likely built from a different repo. +func commitExistsInRepo(commit, repoDir string) bool { + cmd := exec.Command("git", "cat-file", "-t", commit) + cmd.Dir = repoDir + return cmd.Run() == nil +} + // CheckStaleBinary compares the binary's embedded commit with the repo HEAD. // It returns staleness info including whether the binary needs rebuilding. // This check is designed to be fast and non-blocking - errors are captured @@ -93,6 +103,14 @@ func CheckStaleBinary(repoDir string) *StaleBinaryInfo { // Compare commits using prefix matching (handles short vs full hash) // Use the shorter of the two commit lengths for comparison if !commitsMatch(info.BinaryCommit, info.RepoCommit) { + // Check if the binary's commit exists in this repo. + // If it doesn't, the binary was built from a different repo (fork). + // Don't warn about staleness in that case. + if !commitExistsInRepo(info.BinaryCommit, repoDir) { + info.IsForkBuild = true + return info + } + info.IsStale = true // Try to count commits between binary and HEAD