inspect: include a trail link in the post-to-trail confirmation · Entire
Inspect: Include a Trail Link in the Post-to-Trail Confirmation
fb0f37f→main
dipree·1mo ago·2 files·+84 added/-0 removed
When a run posts its verdict to the trail, print a browser link in addition to the existing confirmation, e.g.:
Posted the review verdict to trail #466 as a finding.
View the trail: https://entire.io/gh/entireio/cli/trails/466/review-profiles
trailWebURL builds
Sessions
74c4f7c8f5a9View transcript
?\ Checkout the hand off doc that I just added.Pi·Opus 4.8·2 steps
Changes
2
cmd/entire/cli
Mreview_bridge.go+16
- Areview_bridge_test.go+68
66 unmodified lines
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
66 unmodified lines
} else {
fmt.Fprintln(out, "Posted the review verdict to the trail as a finding.")
}
if link := trailWebURL(target); link != "" {
fmt.Fprintf(out, "View the trail: %s\n", link)
}
return nil
})
}
// trailWebURL builds the browser URL for a trail, matching the server's
// `<base>/<forge>/<owner>/<repo>/trails/<number>/<branch>` layout (the web UI
// shares the API origin). Returns "" when the target lacks the parts needed for
// a stable link.
func trailWebURL(target trailReviewTarget) string {
if target.Trail.Number <= 0 || target.Host == "" || target.Owner == "" || target.Repo == "" {
return ""
}
base := strings.TrimRight(api.BaseURL(), "/")
return fmt.Sprintf("%s/%s/%s/%s/trails/%d/%s",
base, target.Host, target.Owner, target.Repo, target.Trail.Number, target.Trail.Branch)
}
// launchableReviewerFor returns the AgentReviewer for agents with a review-runner
// adapter, or nil for agents that are known to Entire but not yet wired into
// `entire review` fan-out. This lives in the cli package to avoid the import cycle:
Mcmd/entire/cli/review_bridge.go+16
package cli
import (
"testing"
"github.com/entireio/cli/cmd/entire/cli/api"
)
func TestTrailWebURL(t *testing.T) {
t.Setenv(api.BaseURLEnvVar, "https://entire.io")
cases := []struct {
name string
target trailReviewTarget
want string
}{
{
name: "full target",
target: trailReviewTarget{
Host: "gh",
Owner: "entireio",
Repo: "cli",
Trail: api.TrailResource{Number: 466, Branch: "review-profiles"},
},
want: "https://entire.io/gh/entireio/cli/trails/466/review-profiles",
},
{
name: "no trail number yields no link",
target: trailReviewTarget{
Host: "gh",
Owner: "entireio",
Repo: "cli",
Trail: api.TrailResource{Branch: "review-profiles"},
},
want: "",
},
{
name: "missing forge yields no link",
target: trailReviewTarget{
Owner: "entireio",
Repo: "cli",
Trail: api.TrailResource{Number: 1, Branch: "main"},
},
want: "",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := trailWebURL(c.target); got != c.want {
t.Errorf("trailWebURL() = %q, want %q", got, c.want)
}
})
}
}
func TestTrailWebURL_HonorsCustomBase(t *testing.T) {
t.Setenv(api.BaseURLEnvVar, "https://entire.example.com/")
target := trailReviewTarget{
Host: "gh",
Owner: "acme",
Repo: "app",
Trail: api.TrailResource{Number: 7, Branch: "feat/x"},
}
want := "https://entire.example.com/gh/acme/app/trails/7/feat/x"
if got := trailWebURL(target); got != want {
t.Errorf("trailWebURL() = %q, want %q", got, want)
}
}
}
Acmd/entire/cli/review_bridge_test.go+68