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

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")