fix(trail): align update + finding-add with current trails API · Entire
fix(trail): align update + finding-add with current trails API
38e4704→main·
dipree·1mo ago·4 files·+270 added/-72 removed
Two trail commands had drifted from the live data API (verified against https://entire.io/api/openapi.json and live calls):
- trail update PATCHed the single-trail endpoint with the trail id, but
the API keys that route by number and rejects an id with
"Invalid trail number format" (HTTP 400). Send the trail number instead.
- trail finding add POSTed a single flat comment to /reviews/comments,
which no longer exists. Creating a finding now requires starting a review
(POST /reviews) and posting a batch (POST /reviews/{id}/comments,
body {comments:[...]}, response {results:[...]}). Per item, client_id
is required (generated when --client-id is omitted), title was removed,
and suggested_changes (array) became suggested_change (singular). The
review is started with an empty body so the server resolves the code
version — the API rejects a lone head_sha (base_sha and head_sha must be
sent together or not at all).
Read/patch/apply/watch paths were unchanged and remain compatible.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
b1e9c340736dView transcript
Changes
4
cmd/entire/cli
api
Mtrail_review_types.go+72/-9
Mtrail_cmd.go+6/-1
Mtrail_review_cmd.go+97/-29
Mtrail_review_cmd_test.go+95/-33
77 unmodified lines
759 unmodified lines
// Build update request with only changed fields
updateReq := buildTrailUpdateRequest(found, statusStr, title, body, labelAdd, labelRemove)
resp, err := client.Patch(ctx, trailsBasePath(forge, owner, repoName)+"/"+found.ID, updateReq)
// The single-trail endpoint is keyed by trail number, not id; the server
// rejects an id here with "Invalid trail number format".
if found.Number <= 0 {
return fmt.Errorf("trail for branch %q has no number yet; cannot update", branch)
}
resp, err := client.Patch(ctx, trailsBasePath(forge, owner, repoName)+"/"+strconv.Itoa(found.Number), updateReq)
if err != nil {
return fmt.Errorf("failed to update trail: %w", err)
}
// TrailReviewCommentCreateRequest creates an agent-native review finding on a trail.
type TrailReviewCommentCreateRequest struct {
Title *string `json:"title,omitempty"`
Body string `json:"body"`
Severity *string `json:"severity,omitempty"`
Confidence *float64 `json:"confidence,omitempty"`
ClientID *string `json:"client_id,omitempty"`
Location TrailReviewLocationCreateRequest `json:"location"`
SuggestedChanges []TrailReviewSuggestedChangeCreateRequest `json:"suggested_changes,omitempty"`
}
// TrailReviewStartRequest starts a review session for a trail via
// POST /api/v1/trails/{trail_id}/reviews. All fields are optional; the server
// resolves the code version (base/head) when they are omitted.
type TrailReviewStartRequest struct {
HeadSHA *string `json:"head_sha,omitempty"`
BaseSHA *string `json:"base_sha,omitempty"`
BaseRef *string `json:"base_ref,omitempty"`
HeadRef *string `json:"head_ref,omitempty"`
}
// TrailReviewStartResponse is returned by POST /api/v1/trails/{trail_id}/reviews.
type TrailReviewStartResponse struct {
ReviewID string `json:"review_id"`
TrailID string `json:"trail_id"`
RepositoryID string `json:"repository_id"`
CodeVersionID string `json:"code_version_id"`
BaseSHA *string `json:"base_sha"`
HeadSHA *string `json:"head_sha"`
EventStreamURL string `json:"event_stream_url"`
DiffURL string `json:"diff_url"`
FilesURL string `json:"files_url"`
Limits TrailReviewLimits `json:"limits"`
}
// TrailReviewLimits carries the server-enforced batch limits for a review.
type TrailReviewLimits struct {
MaxCommentsPerBatch int `json:"max_comments_per_batch"`
}
// TrailReviewCommentBatchRequest posts a batch of findings to a review via
// POST /api/v1/trails/{trail_id}/reviews/{id}/comments. The API requires at
// least one comment and rejects batches larger than the review's
// max_comments_per_batch limit.
type TrailReviewCommentBatchRequest struct {
Comments []TrailReviewCommentInput `json:"comments"`
}
// TrailReviewCommentInput is a single finding within a batch create request.
// client_id (an idempotency key) and location are required by the API.
type TrailReviewCommentInput struct {
ClientID string `json:"client_id"`
Body *string `json:"body,omitempty"`
Severity *string `json:"severity,omitempty"`
Confidence *float64 `json:"confidence,omitempty"`
Status *string `json:"status,omitempty"`
StatusReason *string `json:"status_reason,omitempty"`
Location TrailReviewLocationCreateRequest `json:"location"`
SuggestedChange *TrailReviewSuggestedChangeCreateRequest `json:"suggested_change,omitempty"`
}
// TrailReviewCommentBatchResponse is returned by the batch comment endpoint.
type TrailReviewCommentBatchResponse struct {
Results []TrailReviewCommentBatchResult `json:"results"`
}
// TrailReviewCommentBatchResult reports the per-finding outcome of a batch.
// Status is one of "created", "existing", or "error"; Comment is populated for
// the first two, Error for the last.
type TrailReviewCommentBatchResult struct {
ClientID string `json:"client_id"`
Status string `json:"status"`
Comment *TrailReviewComment `json:"comment,omitempty"`
SuggestedChange *TrailReviewSuggestedChange `json:"suggested_change,omitempty"`
Error *TrailReviewCommentBatchError `json:"error,omitempty"`
}
// TrailReviewCommentBatchError describes why a single finding in a batch failed.
type TrailReviewCommentBatchError struct {
Code string `json:"code"`
Message string `json:"message"`
Field *string `json:"field"`
Retryable bool `json:"retryable"`
}
// TrailReviewLocationCreateRequest identifies where a new finding applies.