inspect: accept runner-style structured trail findings · Entire

inspect: accept runner-style structured trail findings

956ae3b→main·

dipree·4w ago·4 files·+209 added/-15 removed

Use the existing Trail PR Review runner output shape as the preferred trail posting format: if the judge output ends with a JSON object containing comments, convert those comments directly to native trail review findings, preserving severity, confidence, and line/range/file locations.

Keep the markdown fallback for custom or human-readable prompts, but enrich split bullets with inferred severity and file:line anchors when possible. Also nudge the judge prompt to put one issue per bullet with severity and file:line pointers so trail output is cleaner even before the fallback runs.

Sessions

77524f2a0762View transcript

Changes

4

80 unmodified lines

Output exactly this, nothing else:
  - One line: the verdict (approve / approve with nits / request changes) and a one-sentence reason.
  - Then a short bullet list of actionable findings, most important first, one issue per bullet, one line each with a file/symbol pointer. Omit the list entirely when nothing is actionable.
  - Then a short bullet list of actionable findings, most important first, one issue per bullet, one line each. Start each bullet with [high], [medium], or [low], and include a file:line pointer when you can identify one. Omit the list entirely when nothing is actionable.

No preamble, no section headings, no restating the diff or task, no filler. Be proportional: a clean change is a single line.`)

Mcmd/entire/cli/review/synthesis_prompt.go+1/-1

8 unmodified lines

import (
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "io"
    "regexp"
    "strconv"
    "strings"

"github.com/entireio/cli/cmd/entire/cli/agent"
    5 unmodified lines

reviewtypes "github.com/entireio/cli/cmd/entire/cli/review/types"
)

const (
    reviewTrailGranularityWholeChange = "whole_change"
    reviewTrailGranularityFile        = "file"
    reviewTrailGranularityLine        = "line"
    reviewTrailGranularityRange       = "range"
)

// buildReviewDeps builds the review.Deps struct used by review.NewCommand.
func buildReviewDeps() cliReview.Deps {
    return cliReview.Deps{
    }
}

// reviewTrailFindingInputs turns a final review verdict into trail findings. If
// the verdict contains multiple top-level bullet findings, post them separately
// so a custom or weak judge prompt cannot create one mega-finding on the trail.
// reviewTrailFindingInputs turns a final review verdict into trail findings.
// It first accepts the runner-style last JSON line format
// {"summary":"","comments":[...]}; when absent, it falls back to splitting
// top-level markdown bullets. This keeps trail output structurally correct even
// when custom judge prompts produce prose.
func reviewTrailFindingInputs(profileName, verdict string) []api.TrailReviewCommentInput {
    if inputs, ok := reviewTrailFindingInputsFromJSON(verdict); ok {
        return inputs
    }
    items := splitReviewVerdictFindings(verdict)
    if len(items) <= 1 {
        return []api.TrailReviewCommentInput{reviewTrailFindingInput(profileName, verdict)}
    }
    inputs := make([]api.TrailReviewCommentInput, 0, len(items))
    for _, item := range items {
        inputs = append(inputs, reviewTrailFindingInputWithKind(profileName, item, "finding"))
        input := reviewTrailFindingInputWithKind(profileName, item, "finding")
        enrichReviewTrailFindingInputFromMarkdown(&input, item)
        inputs = append(inputs, input)
    }
    return inputs
}

func reviewTrailFindingInputWithKind(profileName, text, kind string) api.TrailReviewCommentInput {
    body := strings.TrimSpace(text)
    return api.TrailReviewCommentInput{
        ClientID: generateTrailReviewClientID(),
        Body:     stringPtr(body),
        Location: api.TrailReviewLocationCreateRequest{Granularity: reviewTrailGranularityWholeChange},
    }
}

// test functions here...