Files
sanderling/cmd/internal-tools/campaign/manifest.go
T
pj 024913ceeb feat(campaign): kill a run that outlives --run-timeout
A wedged run holds its worker for the rest of the sweep, and on an unattended
host nothing else will send it a signal. Defaults to three times --duration and
must exceed it. A killed run is recorded as timed_out rather than as a generic
failure, so the analysis can tell a lost cell from a real crash.

Claude-Session: https://claude.ai/code/session_01A5KmftdEJ49A9z5mF5ESrX
2026-08-12 21:39:09 +05:30

70 lines
2.3 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)
const (
manifestFileName = "campaign.json"
recordsFileName = "runs.jsonl"
seedPlaceholder = "{seed}"
devicePlaceholder = "{device}"
)
// manifest is campaign.json: what the campaign INTENDED to run, written before
// the first run so a host that dropped runs shows up as missing seeds rather
// than as a smaller sample.
type manifest struct {
Arm string `json:"arm"`
Generator string `json:"generator"`
Platform string `json:"platform"`
SpecPath string `json:"spec_path"`
BundleID string `json:"bundle_id"`
MaxSteps int `json:"max_steps"`
DurationMillis int64 `json:"duration_millis"`
RunTimeoutMillis int64 `json:"run_timeout_millis"`
Seeds []int64 `json:"seeds"`
Devices []string `json:"devices"`
Host string `json:"host"`
SanderlingPath string `json:"sanderling_path"`
SanderlingVersion string `json:"sanderling_version"`
StartedAt time.Time `json:"started_at"`
ArgumentTemplate []string `json:"argument_template"`
}
func buildManifest(configuration config, host, binaryPath, version string, startedAt time.Time) manifest {
devices := configuration.devices
if devices == nil {
devices = []string{}
}
return manifest{
Arm: configuration.arm,
Generator: configuration.generator,
Platform: configuration.platform,
SpecPath: configuration.specPath,
BundleID: configuration.bundleID,
MaxSteps: configuration.maxSteps,
DurationMillis: configuration.duration.Milliseconds(),
RunTimeoutMillis: configuration.runTimeout.Milliseconds(),
Seeds: configuration.seeds,
Devices: devices,
Host: host,
SanderlingPath: binaryPath,
SanderlingVersion: version,
StartedAt: startedAt,
ArgumentTemplate: runArguments(configuration, seedPlaceholder, devicePlaceholder),
}
}
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)
}