fix(inspect): set whole_change granularity when posting a verdict to the trail · Entire
fix(inspect): set whole_change granularity when posting a verdict to the trail
6c85bc9→main·
dipree·1mo ago·2 files·+43 added/-10 removed
Posting the review verdict to a trail failed with 'comments.0.location.granularity: Invalid option' (400): the finding input omitted Location, so an empty granularity was sent. A consolidated verdict isn't tied to a file/line, so set granularity=whole_change (matching the trail finding add path's default). Extracted reviewTrailFindingInput so it's unit tested for granularity + body, and pinned the regression.
Sessions
d4bac437f615View transcript
?\Checkout the hand off doc that I just added.Pi·Opus 4.8·2 steps
Changes
2
cmd/entire/cli
Mreview_bridge.go+17/-10
Mreview_bridge_test.go+26
41 unmodified lines
42
43
44
45
46
45
46
47
49
50
51
52
48
49
50
51
52
53
58
59
60
61
54
55
56
9 unmodified lines
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
41 unmodified lines
// destination. It lives in the cli package because the data API client and
// auth flow do.
func postReviewToTrail(ctx context.Context, out io.Writer, profileName, verdict string) error {
verdict = strings.TrimSpace(verdict)
if verdict == "" {
if strings.TrimSpace(verdict) == "" {
return errors.New("no review output to post")
}
body := verdict
if p := strings.TrimSpace(profileName); p != "" {
body = fmt.Sprintf("Review verdict (profile: %s)\n\n%s", p, verdict)
}
input := reviewTrailFindingInput(profileName, verdict)
return runAuthenticatedDataAPI(ctx, out, false, func(ctx context.Context, client *api.Client) error {
target, err := resolveTrailReviewTarget(ctx, client, "")
if err != nil {
return err
}
input := api.TrailReviewCommentInput{
ClientID: generateTrailReviewClientID(),
Body: stringPtr(body),
}
if _, err := createTrailReviewFinding(ctx, client, target.Trail.ID, input); err != nil {
return err
}
9 unmodified lines
})
}
// reviewTrailFindingInput builds the trail finding payload for a review verdict.
// The verdict spans the whole change, so it uses "whole_change" granularity: the
// API requires a valid granularity and rejects a zero/empty value with a 400.
func reviewTrailFindingInput(profileName, verdict string) api.TrailReviewCommentInput {
body := strings.TrimSpace(verdict)
if p := strings.TrimSpace(profileName); p != "" {
body = fmt.Sprintf("Review verdict (profile: %s)\n\n%s", p, body)
}
return api.TrailReviewCommentInput{
ClientID: generateTrailReviewClientID(),
Body: stringPtr(body),
Location: api.TrailReviewLocationCreateRequest{Granularity: "whole_change"},
}
}
// 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
Mcmd/entire/cli/review_bridge.go+17/-10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package cli
import (
"strings"
"testing"
"github.com/entireio/cli/cmd/entire/cli/api"
)
func TestReviewTrailFindingInput(t *testing.T) {
// Regression: a review verdict is not tied to a file/line, so the finding
// must use whole_change granularity. An empty granularity is rejected by
// the API with a 400.
in := reviewTrailFindingInput("general", " the verdict ")
if in.Location.Granularity != "whole_change" {
t.Errorf("granularity = %q, want whole_change", in.Location.Granularity)
}
if in.ClientID == "" {
}
}
// No profile: the body is exactly the trimmed verdict.
bare := reviewTrailFindingInput("", " bare verdict ")
if bare.Body == nil || *bare.Body != "bare verdict" {
}
}
if bare.Location.Granularity != "whole_change" {
t.Errorf("granularity = %q, want whole_change", bare.Location.Granularity)
}
}
func TestTrailWebURL(t *testing.T) {
t.Setenv(api.BaseURLEnvVar, "https://entire.io")