mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
Preflight cannot catch a device that disappears mid-sweep, which is what happened: the serials were alive the previous day. Three consecutive failures under two minutes, with no run that worked in between, is a property of the device and not a coincidence. The manifest records which device was quarantined and which seeds have no result, so an aborted sweep says so in its own artefact. Claude-Session: https://claude.ai/code/session_01A5KmftdEJ49A9z5mF5ESrX
88 lines
3.2 KiB
Go
88 lines
3.2 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"`
|
|
LabelSource string `json:"label_source"`
|
|
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"`
|
|
|
|
// Quarantined and UnrunSeeds are filled in when the sweep ends, so the
|
|
// artifact says a device dropped out and which seeds have no result rather
|
|
// than leaving both to be inferred from a thin runs.jsonl.
|
|
Quarantined []quarantinedDevice `json:"quarantined,omitempty"`
|
|
UnrunSeeds []int64 `json:"unrun_seeds,omitempty"`
|
|
}
|
|
|
|
// quarantinedDevice is a device the sweep stopped assigning seeds to, with the
|
|
// seeds it consumed on the way out. Those seeds are reported unrun rather than
|
|
// requeued: their directories already hold the failed attempt's log, and a
|
|
// second record for the same seed would make a seed count two runs.
|
|
type quarantinedDevice struct {
|
|
Device string `json:"device"`
|
|
FastFailures int `json:"fast_failures"`
|
|
ConsumedSeeds []int64 `json:"consumed_seeds"`
|
|
}
|
|
|
|
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,
|
|
LabelSource: configuration.labelSource,
|
|
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)
|
|
}
|