Files
sanderling/cmd/internal-tools/confusion-matrix/checker.go
pj 749a173020 fix(confusion-matrix): the same exclusion, so silence is not scored as agreement
A run that spent its budget failing preconditions judged some other app than
the one under test, and its quiet checker was being scored as a true negative
against the reviewer.
2026-08-22 21:12:27 +05:30

358 lines
11 KiB
Go

package main
import (
"bufio"
"encoding/json"
"fmt"
"maps"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
)
const (
sweepManifestFileName = "sweep.json"
sweepRecordsFileName = "implementations.jsonl"
campaignManifestFileName = "campaign.json"
campaignRecordsFileName = "runs.jsonl"
traceFileName = "trace.jsonl"
surfacesExtractor = "locatableSurfaces"
maxRecordBytes = 4 * 1024 * 1024
maxTraceLineBytes = 16 * 1024 * 1024
)
// Run exclusion reasons, kept in the vocabulary analyze already uses so the two
// tools describe the same run the same way.
const (
reasonLaunchError = "launch error"
reasonTimedOut = "timed out"
reasonNonzeroExit = "nonzero exit"
reasonTraceError = "unreadable trace"
// reasonPreconditionFailures is the run that exited cleanly having spent its
// budget failing preconditions, so its properties judged some other app than
// the one under test. Scoring the silence as a checker that fired on nothing
// reads a run that never explored as agreement with the reviewer.
reasonPreconditionFailures = "precondition failures"
)
type sweepManifest struct {
SpecPath string `json:"spec_path"`
Implementations []struct {
Name string `json:"name"`
} `json:"implementations"`
}
type sweepRunRecord struct {
Seed int64 `json:"seed"`
ExitCode int `json:"exit_code"`
LaunchError string `json:"launch_error"`
CampaignDirectory string `json:"campaign_directory"`
}
type sweepImplementationRecord struct {
Name string `json:"implementation"`
FailedStage string `json:"failed_stage"`
Error string `json:"error"`
Runs []sweepRunRecord `json:"runs"`
}
type campaignRunRecord struct {
Seed int64 `json:"seed"`
ExitCode int `json:"exit_code"`
LaunchError string `json:"launch_error"`
TimedOut bool `json:"timed_out"`
TraceError string `json:"trace_error"`
Steps int `json:"steps"`
// PreconditionFailures is how many of the run's steps never had the app under
// test in front of them. The campaign omits the field when it is zero, so
// absence and zero mean the same thing here.
PreconditionFailures int `json:"precondition_failures"`
RunDirectory string `json:"run_directory"`
ViolatedProperties []string `json:"violated_properties"`
}
// checkerVerdict is one implementation's whole checker side, pooled across the
// seeds it was swept at.
type checkerVerdict struct {
Implementation string
FailedStage string
FailedError string
RunsRecorded int
RunsUsable int
ExcludedByReason map[string]int
FiredProperties []string
// SurfacesObserved holds every locatable surface seen true on at least one
// step of at least one usable run. A surface missing from it was never
// located across the whole sweep of this implementation.
SurfacesObserved map[string]bool
SurfacesKnown bool
TraceErrors []string
}
func (v checkerVerdict) fired() bool { return len(v.FiredProperties) > 0 }
type checkerSide struct {
Directory string
SpecPath string
Planned []string
Verdicts []checkerVerdict
}
func loadChecker(directory string) (checkerSide, error) {
body, err := os.ReadFile(filepath.Join(directory, sweepManifestFileName))
if err != nil {
return checkerSide{}, fmt.Errorf("read %s: %w", sweepManifestFileName, err)
}
var declared sweepManifest
if err := json.Unmarshal(body, &declared); err != nil {
return checkerSide{}, fmt.Errorf("parse %s in %s: %w", sweepManifestFileName, directory, err)
}
side := checkerSide{Directory: directory, SpecPath: declared.SpecPath}
for _, planned := range declared.Implementations {
side.Planned = append(side.Planned, planned.Name)
}
records, err := readSweepRecords(filepath.Join(directory, sweepRecordsFileName))
if err != nil {
return checkerSide{}, err
}
for _, record := range records {
verdict, err := readImplementation(directory, record)
if err != nil {
return checkerSide{}, err
}
side.Verdicts = append(side.Verdicts, verdict)
}
slices.SortFunc(side.Verdicts, func(a, b checkerVerdict) int {
return strings.Compare(a.Implementation, b.Implementation)
})
return side, nil
}
func readSweepRecords(path string) ([]sweepImplementationRecord, error) {
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("read %s: %w", sweepRecordsFileName, err)
}
defer file.Close()
var records []sweepImplementationRecord
scanner := bufio.NewScanner(file)
scanner.Buffer(make([]byte, 0, 64*1024), maxRecordBytes)
lineNumber := 0
seen := map[string]bool{}
for scanner.Scan() {
lineNumber++
raw := strings.TrimSpace(scanner.Text())
if raw == "" {
continue
}
var record sweepImplementationRecord
if err := json.Unmarshal([]byte(raw), &record); err != nil {
return nil, fmt.Errorf("%s line %d: %w", sweepRecordsFileName, lineNumber, err)
}
if record.Name == "" {
return nil, fmt.Errorf("%s line %d names no implementation", sweepRecordsFileName, lineNumber)
}
if seen[record.Name] {
return nil, fmt.Errorf("%s line %d records %s a second time: its runs would be pooled twice",
sweepRecordsFileName, lineNumber, record.Name)
}
seen[record.Name] = true
records = append(records, record)
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("read %s: %w", sweepRecordsFileName, err)
}
return records, nil
}
func readImplementation(sweepDirectory string, record sweepImplementationRecord) (checkerVerdict, error) {
verdict := checkerVerdict{
Implementation: record.Name,
FailedStage: record.FailedStage,
FailedError: record.Error,
ExcludedByReason: map[string]int{},
SurfacesObserved: map[string]bool{},
}
fired := map[string]bool{}
for _, run := range record.Runs {
verdict.RunsRecorded++
if reason := sweepRunExcludedBecause(run); reason != "" {
verdict.ExcludedByReason[reason]++
continue
}
directory := resolveCampaignDirectory(sweepDirectory, record.Name, run)
campaignRuns, err := readCampaignRuns(directory)
if err != nil {
return checkerVerdict{}, err
}
for _, campaignRun := range campaignRuns {
if reason := excludedBecause(campaignRun); reason != "" {
verdict.ExcludedByReason[reason]++
continue
}
verdict.RunsUsable++
for _, property := range campaignRun.ViolatedProperties {
fired[property] = true
}
observed, err := readObservedSurfaces(filepath.Join(directory, campaignRun.RunDirectory))
if err != nil {
verdict.TraceErrors = append(verdict.TraceErrors, err.Error())
continue
}
verdict.SurfacesKnown = true
for surface, seen := range observed {
if seen {
verdict.SurfacesObserved[surface] = true
}
}
}
}
if len(fired) > 0 {
verdict.FiredProperties = slices.Sorted(maps.Keys(fired))
}
if len(verdict.ExcludedByReason) == 0 {
verdict.ExcludedByReason = nil
}
return verdict, nil
}
// resolveCampaignDirectory prefers the path the sweep recorded and falls back to
// the layout it names, so a sweep directory read on another machine than the one
// that wrote its absolute paths still resolves.
func resolveCampaignDirectory(sweepDirectory, name string, run sweepRunRecord) string {
if run.CampaignDirectory != "" {
if _, err := os.Stat(run.CampaignDirectory); err == nil {
return run.CampaignDirectory
}
}
return filepath.Join(sweepDirectory, name, "seed-"+strconv.FormatInt(run.Seed, 10))
}
func readCampaignRuns(directory string) ([]campaignRunRecord, error) {
if _, err := os.Stat(filepath.Join(directory, campaignManifestFileName)); err != nil {
return nil, fmt.Errorf("read %s in %s: %w", campaignManifestFileName, directory, err)
}
file, err := os.Open(filepath.Join(directory, campaignRecordsFileName))
if err != nil {
return nil, fmt.Errorf("read %s: %w", campaignRecordsFileName, err)
}
defer file.Close()
var records []campaignRunRecord
scanner := bufio.NewScanner(file)
scanner.Buffer(make([]byte, 0, 64*1024), maxRecordBytes)
lineNumber := 0
for scanner.Scan() {
lineNumber++
raw := strings.TrimSpace(scanner.Text())
if raw == "" {
continue
}
var record campaignRunRecord
if err := json.Unmarshal([]byte(raw), &record); err != nil {
return nil, fmt.Errorf("%s line %d in %s: %w", campaignRecordsFileName, lineNumber, directory, err)
}
records = append(records, record)
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("read %s in %s: %w", campaignRecordsFileName, directory, err)
}
return records, nil
}
// sweepRunExcludedBecause reads the outcome of the campaign process itself,
// which the records inside its directory cannot report. One sweep run is one
// campaign of one seed, so a campaign that died left a runs.jsonl that is
// partial or empty, and scoring the runs it did write reads the seeds it never
// reached as agreement.
func sweepRunExcludedBecause(record sweepRunRecord) string {
switch {
case record.LaunchError != "":
return reasonLaunchError
case record.ExitCode != 0:
return reasonNonzeroExit
default:
return ""
}
}
func excludedBecause(record campaignRunRecord) string {
switch {
case record.LaunchError != "":
return reasonLaunchError
case record.TimedOut:
return reasonTimedOut
case record.ExitCode != 0:
return reasonNonzeroExit
case record.TraceError != "":
return reasonTraceError
case record.PreconditionFailures*2 > record.Steps:
return reasonPreconditionFailures
default:
return ""
}
}
type traceLine struct {
ExtractorChanges map[string]struct {
Curr json.RawMessage `json:"curr"`
} `json:"extractor_changes"`
}
// readObservedSurfaces replays one run's locatableSurfaces readings. The
// verifier emits every extractor as a change on the first snapshot and only on
// a difference afterwards, so a surface true on any recorded change was located
// at least once, and one absent from every change was never located at all.
func readObservedSurfaces(runDirectory string) (map[string]bool, error) {
path := filepath.Join(runDirectory, traceFileName)
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("read %s: %w", path, err)
}
defer file.Close()
observed := map[string]bool{}
found := false
scanner := bufio.NewScanner(file)
scanner.Buffer(make([]byte, 0, 64*1024), maxTraceLineBytes)
lineNumber := 0
for scanner.Scan() {
lineNumber++
raw := strings.TrimSpace(scanner.Text())
if raw == "" {
continue
}
var line traceLine
if err := json.Unmarshal([]byte(raw), &line); err != nil {
return nil, fmt.Errorf("%s line %d: %w", path, lineNumber, err)
}
change, present := line.ExtractorChanges[surfacesExtractor]
if !present || len(change.Curr) == 0 {
continue
}
var reading map[string]bool
if err := json.Unmarshal(change.Curr, &reading); err != nil {
return nil, fmt.Errorf("%s line %d: %s is not an object of booleans: %w",
path, lineNumber, surfacesExtractor, err)
}
found = true
for surface, located := range reading {
if located {
observed[surface] = true
}
}
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("read %s: %w", path, err)
}
if !found {
return nil, fmt.Errorf("%s records no %s reading: this run cannot say whether a surface was located",
path, surfacesExtractor)
}
return observed, nil
}