Documentation
¶
Overview ¶
Package eval scores a directory of review prompts against a suite of changes whose problems are known, so prompts can be compared by what they catch and what they send back that they should not.
Each case is a change to a repository at a pinned revision: a patch, the request it answers, and the problems a good review should find in it. A case that expects none is clean, and any high or medium finding on it sends sound work back for nothing. Reviews run as factory runs them in its first round, against the patch as uncommitted work, but with no implementer: the score is the reviews' alone.
Index ¶
Constants ¶
const SuiteFile = "suite.json"
SuiteFile is the file in a suite directory that lists its cases.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Case ¶
type Case struct {
Name string `json:"name"`
// Revision is the commit the patch applies to.
Revision string `json:"revision"`
// Request is the change the patch answers, which reviewers are given as
// the specification, as factory gives them the developer's prompt.
Request string `json:"request"`
// Patch is the change, a path to a git diff relative to the suite
// directory.
Patch string `json:"patch"`
// Source says where the case came from, such as a pull request or a
// mutation, for whoever reads the results.
Source string `json:"source,omitempty"`
// Expect are the problems a review should find. Empty means the change is
// sound and should come back with no high or medium findings.
Expect []Expected `json:"expect,omitempty"`
// contains filtered or unexported fields
}
Case is one change to review.
type CaseResult ¶
type CaseResult struct {
Case string `json:"case"`
Run int `json:"run"`
// Clean is whether the case expects no problems.
Clean bool `json:"clean"`
// Errors are the reviews that could not run. A case with any is left out
// of the summary rather than scored on the reviews that did, which would
// make a broken prompt look like a quiet one.
Errors []PromptError `json:"errors,omitempty"`
// Problems are what the blocking findings were about, each once, with
// every finding about it: an expected problem one caught, or one the case
// does not list. An unlisted problem on a clean case sends sound work
// back. On another it may be real, so it is worth reading rather than
// counting blindly.
Problems []Problem `json:"problems,omitempty"`
// Missed are the expected problems no blocking finding was about, in the
// case's order.
Missed []Expected `json:"missed,omitempty"`
// Findings is everything the reviews reported, blocking or not.
Findings []review.Finding `json:"findings"`
}
CaseResult is how one run of the reviews did on one case.
func Run ¶
func Run(ctx context.Context, opts Options) ([]CaseResult, error)
Run reviews every case in the suite with the prompts, Runs times over, and scores each. A case whose setup or reviews fail is recorded as errored and the suite goes on. The returned error is for failures that would fail every case the same way, such as a missing claude or a rejected credential.
func Score ¶
func Score(c Case, run int, results []review.Result) CaseResult
Score groups the reviews' blocking findings on c into problems and matches them against what c expects. Only high and medium findings count: they are what factory sends back.
func (CaseResult) Caught ¶
func (r CaseResult) Caught() int
Caught counts the expected problems a blocking finding was about.
func (CaseResult) Errored ¶
func (r CaseResult) Errored() bool
Errored reports whether any review failed on the case.
func (CaseResult) Repeats ¶
func (r CaseResult) Repeats() int
Repeats counts blocking findings about a problem an earlier one already reported.
func (CaseResult) Unexpected ¶
func (r CaseResult) Unexpected() int
Unexpected counts the problems the case does not list.
type Expected ¶
type Expected struct {
// File is the repository-relative path the problem is in.
File string `json:"file"`
// Lines is the range of new-file lines the problem is on, first and last.
// Empty means anywhere in the file.
Lines []int `json:"lines,omitempty"`
// About says what the problem is, for whoever reads the results.
About string `json:"about"`
}
Expected is one problem a case's change has.
type Options ¶
type Options struct {
// Root is the local repository the suite's revisions are in.
Root string
Suite Suite
Prompts []review.Prompt
// Runs is how many times each case is reviewed; one when zero. Reviews
// vary from run to run, so comparing prompts on one run each compares
// luck as much as prompts.
Runs int
// Entries are the sidecars the reviews run on. Every case is put on all
// of them, and its reviews share them; one per prompt runs them all at
// once.
Entries []*sidecar.PoolEntry
Exec sidecar.Execer
// Sync mirrors the local directory dir into entry's workspace, leaving
// the workspace's .git alone, as a pool's sync does.
Sync func(ctx context.Context, entry *sidecar.PoolEntry, dir string) error
Review review.Options
Status iostream.StatusFunc
// OnCase is called with each case's result as it is scored.
OnCase func(CaseResult)
}
Options configures scoring a suite.
type Problem ¶
type Problem struct {
// Expected is the case's problem this is; nil when the case does not list
// it.
Expected *Expected `json:"expected,omitempty"`
// Findings are the blocking findings about it, the first first. More than
// one is the same problem reported again, by the same prompt or another.
Findings []review.Finding `json:"findings"`
}
Problem is one problem the blocking findings on a case were about.
type PromptError ¶
PromptError is a review that failed on a case.
type PromptSummary ¶
type PromptSummary struct {
// Blocking counts its high and medium findings.
Blocking int `json:"blocking"`
// Caught counts the expected problems it reported, and Unexpected the
// unlisted ones.
Caught int `json:"caught"`
Unexpected int `json:"unexpected"`
// OnClean counts the problems it reported on clean cases, each of which
// would have sent sound work back.
OnClean int `json:"on_clean"`
// Shared counts the problems it reported that another prompt reported
// too.
Errors int `json:"errors"`
}
PromptSummary is how one review prompt did across a suite's scored runs. A problem counts for every prompt that reported it, not only the first.
type Suite ¶
Suite is a set of cases on one repository.
type Summary ¶
type Summary struct {
// Runs counts case runs, Scored those every review finished, and Errored
// the rest, which the other counts leave out.
Runs int `json:"runs"`
Scored int `json:"scored"`
Errored int `json:"errored"`
// Expected counts the problems in scored runs, and Caught those a
// blocking finding was about.
Expected int `json:"expected"`
Caught int `json:"caught"`
// Recall is Caught over Expected; absent when no scored run expected a
// problem.
Recall *float64 `json:"recall,omitempty"`
// Blocking counts high and medium findings in scored runs, and Reported
// the problems they were about, each once. Unexpected are the reported
// problems the suite does not list, and Repeats the findings beyond the
// first about each problem.
Blocking int `json:"blocking"`
Reported int `json:"reported"`
Unexpected int `json:"unexpected"`
Repeats int `json:"repeats"`
// the prompts overlap.
Shared int `json:"shared"`
// Precision is the share of reported problems the suite lists; absent
// when nothing was reported. It is a floor: an unexpected problem may be
// a real one the suite does not list.
Precision *float64 `json:"precision,omitempty"`
// Clean counts scored runs of clean cases, and CleanPassed those with no
// blocking finding at all: sound work factory would have let through.
Clean int `json:"clean"`
CleanPassed int `json:"clean_passed"`
// Prompts breaks the counts down by review prompt, so a noisy, a blind or
// a redundant one can be told from the rest.
Prompts map[string]*PromptSummary `json:"prompts"`
}
Summary is how the reviews did across a suite.
func Summarize ¶
func Summarize(prompts []string, results []CaseResult) Summary
Summarize totals results. prompts names every review prompt that ran, so one that never reported anything still has a row.