fix. harden Go review flow
Fix runner bootstrap and auth handling, preserve queued SHAs, make event/job acceptance atomic, fence stale runs, correct retries and prompts, add fake end-to-end coverage, and fix deployment defaults. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+63
-26
@@ -44,11 +44,22 @@ func ParseRepoConfig(text string) (domain.RepoReviewConfig, error) {
|
||||
cfg.Ignore = boundedStrings(raw.Ignore, 128, 500)
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func MissingRepoConfig() domain.RepoReviewConfig {
|
||||
cfg := domain.DefaultRepoReviewConfig()
|
||||
cfg.Configured = false
|
||||
return cfg
|
||||
}
|
||||
|
||||
func ApplyServerMaxDiff(cfg *domain.RepoReviewConfig, serverMax int) {
|
||||
if serverMax <= 0 {
|
||||
return
|
||||
}
|
||||
if cfg.MaxDiffBytes <= 0 || cfg.MaxDiffBytes > serverMax {
|
||||
cfg.MaxDiffBytes = serverMax
|
||||
}
|
||||
}
|
||||
|
||||
func boundedStrings(input []string, maxItems, maxLen int) []string {
|
||||
out := make([]string, 0, min(len(input), maxItems))
|
||||
for _, item := range input {
|
||||
@@ -62,6 +73,7 @@ func boundedStrings(input []string, maxItems, maxLen int) []string {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func ResolveMode(cmd *domain.ParsedCommand, cfg domain.RepoReviewConfig) {
|
||||
if cmd.Name == "review" && !cmd.ModeExplicit {
|
||||
if cfg.DefaultMode == "full" || cfg.DefaultMode == "summary" || cfg.DefaultMode == "security" || cfg.DefaultMode == "performance" || cfg.DefaultMode == "tests" {
|
||||
@@ -71,15 +83,9 @@ func ResolveMode(cmd *domain.ParsedCommand, cfg domain.RepoReviewConfig) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BuildPrompt(cmd domain.ParsedCommand, cfg domain.RepoReviewConfig, pr domain.PullRequestContext) string {
|
||||
raw := strings.TrimSpace(cmd.Raw)
|
||||
intent := raw
|
||||
if at := strings.IndexAny(raw, " \t\r\n"); at >= 0 {
|
||||
intent = strings.TrimSpace(raw[at:])
|
||||
}
|
||||
if at := strings.IndexAny(intent, " \t\r\n"); at >= 0 {
|
||||
intent = strings.TrimSpace(intent[at:])
|
||||
}
|
||||
intent := commandIntent(cmd.Raw)
|
||||
if intent == "" {
|
||||
intent = "review this pull request and report introduced issues."
|
||||
}
|
||||
@@ -95,10 +101,28 @@ func BuildPrompt(cmd domain.ParsedCommand, cfg domain.RepoReviewConfig, pr domai
|
||||
if cmd.Mode == "tests" || cfg.IncludeTests {
|
||||
tests = "Tests may be executed for this run because tests mode/include_tests is explicitly enabled."
|
||||
}
|
||||
return fmt.Sprintf("review: %s\nReview only issues introduced by this PR.\nCompare exactly these commits: base `%s` ... head `%s`.\nUse local git data from this checkout; do not review unrelated history.\nRequested mode: %s.\nFocus areas: %s.\nIgnore patterns: %s.\nInclude tests setting: %t.\n%s\nFull review requested: %t.\nReturn strict JSON matching the provided output schema.", intent, pr.BaseSHA, pr.HeadSHA, cmd.Mode, focus, ignore, cfg.IncludeTests, tests, cmd.Full)
|
||||
return fmt.Sprintf("review: %s\nReview only issues introduced by this PR.\nCompare exactly these commits: base `%s` ... head `%s`.\nUse local git data from this checkout; do not review unrelated history.\nRequested mode: %s.\nFocus areas: %s.\nIgnore patterns: %s.\nMaximum diff bytes: %d.\nInclude tests setting: %t.\n%s\nFull review requested: %t.\nReturn strict JSON matching the provided output schema.", intent, pr.BaseSHA, pr.HeadSHA, cmd.Mode, focus, ignore, cfg.MaxDiffBytes, cfg.IncludeTests, tests, cmd.Full)
|
||||
}
|
||||
|
||||
func commandIntent(raw string) string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
aliasEnd := strings.IndexAny(raw, " \t\r\n")
|
||||
if aliasEnd < 0 {
|
||||
return ""
|
||||
}
|
||||
remainder := strings.TrimSpace(raw[aliasEnd:])
|
||||
commandEnd := strings.IndexAny(remainder, " \t\r\n")
|
||||
if commandEnd < 0 {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(remainder[commandEnd:])
|
||||
}
|
||||
|
||||
func ValidateResult(result domain.ReviewResult) error { return result.Validate() }
|
||||
|
||||
func DecodeResult(data []byte) (domain.ReviewResult, error) {
|
||||
var result domain.ReviewResult
|
||||
dec := json.NewDecoder(strings.NewReader(string(data)))
|
||||
@@ -111,6 +135,7 @@ func DecodeResult(data []byte) (domain.ReviewResult, error) {
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func FormatQueueAck(sha string) string {
|
||||
return fmt.Sprintf("👀 Codex review queued for commit `%s`.", first(sha, 7))
|
||||
}
|
||||
@@ -123,28 +148,14 @@ func FormatDisabledAck() string {
|
||||
func FormatUnsupportedAck(name string) string {
|
||||
return fmt.Sprintf("⚠️ Command `@codex %s` is not enabled on this repository.", name)
|
||||
}
|
||||
|
||||
func FormatResultComment(sha string, result domain.ReviewResult, configured bool) string {
|
||||
marker := fmt.Sprintf("<!-- codex-review:head_sha=%s -->", sha)
|
||||
body := strings.TrimSpace(result.MarkdownComment)
|
||||
if body == "" {
|
||||
body = fmt.Sprintf("## Codex Review\n\nVerdict: `%s`\nConfidence: `%.2f`\n\n%s", result.Verdict, result.Confidence, result.Summary)
|
||||
if len(result.Findings) == 0 {
|
||||
body += "\n\nNo blocking issues found."
|
||||
} else {
|
||||
body += "\n\nFindings:"
|
||||
for i, f := range result.Findings {
|
||||
suggestion := "n/a"
|
||||
if f.Suggestion != nil && *f.Suggestion != "" {
|
||||
suggestion = *f.Suggestion
|
||||
}
|
||||
body += fmt.Sprintf("\n\n%d. `%s:%d-%d` (%s)\n %s\n %s\n Suggestion: %s", i+1, f.File, f.LineStart, f.LineEnd, f.Severity, f.Title, f.Body, suggestion)
|
||||
}
|
||||
}
|
||||
body = fallbackDetails(result)
|
||||
} else if len(result.Findings) > 0 {
|
||||
body += "\n\n---\n\n### Structured Findings\n\n"
|
||||
for i, f := range result.Findings {
|
||||
body += fmt.Sprintf("%d. `%s:%d-%d` (%s)\n %s\n %s\n\n", i+1, f.File, f.LineStart, f.LineEnd, f.Severity, f.Title, f.Body)
|
||||
}
|
||||
body += "\n\n---\n\n" + structuredDetails(result)
|
||||
}
|
||||
if result.Meta != nil && result.Meta.Model != "" {
|
||||
body += fmt.Sprintf("\n_Note: model `%s`, input `%d`, output `%d`, total `%d` tokens used._", result.Meta.Model, result.Meta.Usage.InputTokens, result.Meta.Usage.OutputTokens, result.Meta.Usage.TotalTokens)
|
||||
@@ -162,6 +173,32 @@ func FormatResultComment(sha string, result domain.ReviewResult, configured bool
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func fallbackDetails(result domain.ReviewResult) string {
|
||||
body := fmt.Sprintf("## Codex Review\n\nVerdict: `%s`\nConfidence: `%.2f`\n\n%s", result.Verdict, result.Confidence, result.Summary)
|
||||
if len(result.Findings) == 0 {
|
||||
return body + "\n\nNo blocking issues found."
|
||||
}
|
||||
return body + "\n\n" + structuredFindings(result.Findings)
|
||||
}
|
||||
|
||||
func structuredDetails(result domain.ReviewResult) string {
|
||||
return fmt.Sprintf("### Structured Findings\n\nVerdict: `%s`\nConfidence: `%.2f`\n\n%s\n\n%s", result.Verdict, result.Confidence, result.Summary, structuredFindings(result.Findings))
|
||||
}
|
||||
|
||||
func structuredFindings(findings []domain.Finding) string {
|
||||
var body strings.Builder
|
||||
body.WriteString("Findings:")
|
||||
for i, f := range findings {
|
||||
suggestion := "n/a"
|
||||
if f.Suggestion != nil && *f.Suggestion != "" {
|
||||
suggestion = *f.Suggestion
|
||||
}
|
||||
fmt.Fprintf(&body, "\n\n%d. `%s:%d-%d` (%s)\n %s\n %s\n Suggestion: %s", i+1, f.File, f.LineStart, f.LineEnd, f.Severity, f.Title, f.Body, suggestion)
|
||||
}
|
||||
return body.String()
|
||||
}
|
||||
|
||||
func FailureComment(sha, errText string) string {
|
||||
return fmt.Sprintf("⚠️ Codex review run failed after queueing.\n\n- Commit: `%s`\n- Error: `%s`\n\nPlease rerun `@codex rerun` after checking worker logs.", first(sha, 7), first(strings.Join(strings.Fields(errText), " "), 500))
|
||||
}
|
||||
|
||||
@@ -35,6 +35,24 @@ func TestResultValidationAndFormatting(t *testing.T) {
|
||||
t.Fatalf("missing formatted details: %s", body)
|
||||
}
|
||||
}
|
||||
func TestBuildPromptUsesGenericIntentForBareReview(t *testing.T) {
|
||||
prompt := BuildPrompt(domain.ParsedCommand{Name: "review", Raw: "@codex review", Mode: "summary"}, domain.DefaultRepoReviewConfig(), domain.PullRequestContext{BaseSHA: "base", HeadSHA: "head"})
|
||||
if contains(prompt, "review: review\n") || !contains(prompt, "review: review this pull request and report introduced issues.") {
|
||||
t.Fatalf("unexpected bare-review prompt: %s", prompt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarkdownCommentIncludesStructuredSummaryAndSuggestion(t *testing.T) {
|
||||
suggestion := "Use a checked conversion."
|
||||
result := domain.ReviewResult{Verdict: "has_issues", Confidence: .8, Summary: "Summary", MarkdownComment: "Primary markdown", Findings: []domain.Finding{{Severity: "medium", File: "x.go", LineStart: 2, LineEnd: 2, Title: "Issue", Body: "Details", Suggestion: &suggestion}}}
|
||||
body := FormatResultComment("head", result, true)
|
||||
for _, part := range []string{"Structured Findings", "Verdict: `has_issues`", "Summary", "Use a checked conversion."} {
|
||||
if !contains(body, part) {
|
||||
t.Fatalf("formatted comment missing %q: %s", part, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func contains(s, needle string) bool {
|
||||
for i := 0; i+len(needle) <= len(s); i++ {
|
||||
if s[i:i+len(needle)] == needle {
|
||||
|
||||
Reference in New Issue
Block a user