Files
sanderling/cmd/internal-tools/implementation-sweep/manifest.go
T
pj 146152a3af feat(implementation-sweep): run one campaign against every implementation of a requirement
installs, builds and serves each implementation on its own port, then hands the campaign tool the same seed slice, step budget and generator for all of them, so a difference between implementations is not a difference in exploration. the generator and platform are fixed rather than exposed.
2026-08-16 17:45:41 +05:30

118 lines
3.9 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)
const (
manifestFileName = "sweep.json"
recordsFileName = "implementations.jsonl"
seedPlaceholder = "{seed}"
)
// plannedImplementation is one implementation the sweep intends to run, with
// the port it is served on and the URL every seed is served at.
type plannedImplementation struct {
Name string `json:"name"`
Directory string `json:"directory"`
Port int `json:"port"`
URLTemplate string `json:"url_template"`
}
// manifest is sweep.json: what the sweep INTENDED to run, written before the
// first install so a host that dropped an implementation or a seed shows up as
// a missing run rather than as a smaller sample.
type manifest struct {
Generator string `json:"generator"`
Platform string `json:"platform"`
SpecPath string `json:"spec_path"`
MaxSteps int `json:"max_steps"`
DurationMillis int64 `json:"duration_millis"`
Seeds []int64 `json:"seeds"`
Implementations []plannedImplementation `json:"implementations"`
Concurrency int `json:"concurrency"`
Host string `json:"host"`
BunPath string `json:"bun_path"`
CampaignPath string `json:"campaign_path"`
SanderlingPath string `json:"sanderling_path"`
StartedAt time.Time `json:"started_at"`
}
func buildManifest(
configuration config,
implementations []implementation,
host string,
startedAt time.Time,
) manifest {
planned := make([]plannedImplementation, 0, len(implementations))
for _, target := range implementations {
planned = append(planned, plannedImplementation{
Name: target.Name,
Directory: target.Directory,
Port: target.Port,
URLTemplate: servedURL(target.Port, seedPlaceholder),
})
}
return manifest{
Generator: generator,
Platform: platform,
SpecPath: configuration.specPath,
MaxSteps: configuration.maxSteps,
DurationMillis: configuration.duration.Milliseconds(),
Seeds: configuration.seeds,
Implementations: planned,
Concurrency: configuration.concurrency,
Host: host,
BunPath: configuration.bunPath,
CampaignPath: configuration.campaignPath,
SanderlingPath: configuration.sanderlingPath,
StartedAt: startedAt,
}
}
func writeManifest(directory string, value manifest) error {
body, err := json.MarshalIndent(value, "", " ")
if err != nil {
return fmt.Errorf("marshal manifest: %w", err)
}
return os.WriteFile(
filepath.Join(directory, manifestFileName),
append(body, '\n'),
0o644,
)
}
// runRecord is one campaign, which is one implementation at one seed.
type runRecord struct {
Seed int64 `json:"seed"`
URL string `json:"url"`
ExitCode int `json:"exit_code"`
LaunchError string `json:"launch_error,omitempty"`
CampaignDirectory string `json:"campaign_directory"`
MonotonicMillis int64 `json:"monotonic_millis"`
}
// implementationRecord is one line of implementations.jsonl. FailedStage names
// the step that stopped this implementation, and an implementation that never
// got past install, build or serve carries no runs at all.
type implementationRecord struct {
Name string `json:"implementation"`
Directory string `json:"directory"`
Port int `json:"port"`
FailedStage string `json:"failed_stage,omitempty"`
Error string `json:"error,omitempty"`
StartedAt time.Time `json:"started_at"`
MonotonicMillis int64 `json:"monotonic_millis"`
Runs []runRecord `json:"runs"`
}
const (
stageInstall = "install"
stageBuild = "build"
stageServe = "serve"
)