feat(review): tell reviewers their time budget in the prompt · Entire

feat(review): tell reviewers their time budget in the prompt

5594b76·

peyton-alt·1w ago·4 files·+81 added/-1 removed

A reviewer that knows its deadline paces itself; one that doesn't runs
its full-depth pipeline and gets killed mid-verify with every finding
discarded — observed live: a claude-code review was ~90% done when the
timeout killed it and the run recorded nothing from those 10 minutes.

ComposeReviewPrompt now renders the effective per-reviewer timeout as a
pacing instruction. Only a positive RunConfig.ReviewerTimeout renders
(zero = unresolved, negative = disabled; inventing a default in either
case could contradict the orchestrator's real deadline), and the
multi-agent path now sets ReviewerTimeout on each per-agent RunConfig —
previously only the single-agent path carried it.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

Sessions

1013984a9665View transcript

Changes

4

1205 unmodified lines

1206
1207
1208
1209
1210
1211
1212

1205 unmodified lines

CheckpointContext: checkpointContext,
                ScopeContext:      scopeCtx,
                StartingSHA:       headSHA,
                ReviewerTimeout:   timeout,
            }, agentCfg),
        })
    }

Mcmd/entire/cli/review/cmd.go+1

800 unmodified lines

801
802
803
804
804
805
806
807
22 unmodified lines

830
831
832
833
834
835
836
837
838
839
840

800 unmodified lines

cmd := review.NewCommand(deps)
    cmd.SetOut(&bytes.Buffer{})
    cmd.SetErr(&bytes.Buffer{})
    cmd.SetArgs([]string{"general", "--prompt", "Focus this run on regressions."})
    cmd.SetArgs([]string{"general", "--prompt", "Focus this run on regressions.", "--timeout", "7m"})

if err := cmd.Execute(); err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
22 unmodified lines

if tc.reviewer.got.StartingSHA == "" {
            t.Fatalf("%s StartingSHA is empty", tc.name)
        }
        // The per-agent RunConfig must carry the effective timeout so the
        // composed prompt can tell the child its deadline (time-budget hint).
        if tc.reviewer.got.ReviewerTimeout != 7*time.Minute {
            t.Fatalf("%s ReviewerTimeout = %v, want 7m", tc.name, tc.reviewer.got.ReviewerTimeout)
        }
    }
}

Mcmd/entire/cli/review/cmd_test.go+6/-1

9 unmodified lines

10
11
12
13
14
15
16
17
18
51 unmodified lines

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

9 unmodified lines

package review

import (
    "fmt"
    "strings"
    "time"

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

if trimmed := strings.TrimRight(cfg.CheckpointContext, "\n\r "); trimmed != "" {
        sections = append(sections, trimmed)
    }
    if hint := renderTimeBudgetHint(cfg.ReviewerTimeout); hint != "" {
        sections = append(sections, hint)
    }

return strings.Join(sections, "\n\n")
}

// renderTimeBudgetHint tells the agent its hard deadline so it can pace its
// review instead of running a full-depth pipeline and being killed mid-pass
// with all work discarded. Only a positive timeout renders: zero means the
// caller didn't resolve one, negative means timeouts are disabled, and
// inventing a default in either case could contradict the orchestrator's
// actual deadline.
func renderTimeBudgetHint(timeout time.Duration) string {
    if timeout <= 0 {
        return ""
    }
    deadline := timeout.String()
    if timeout%time.Minute == 0 {
        minutes := int(timeout / time.Minute)
        unit := "minutes"
        if minutes == 1 {
            unit = "minute"
        }
        deadline = fmt.Sprintf("%d %s", minutes, unit)
    }
    return "Time budget: this review is cancelled after " + deadline +
        ". Pace yourself to deliver the final report comfortably within that limit — a complete report at reduced depth beats an unfinished deep pass."
}

// renderScopeContext renders the parent-computed scope enumeration. Handing
// agents the concrete commit/file lists (instead of only describing how to
// derive them) removes both the re-derivation cost at the start of every run