mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 20:17:09 +00:00
feat(campaign): sweep seeds for one experiment cell
campaign.json lists the seeds a sweep intended to run and is written before the first run, so a host that dropped runs shows up as missing seeds rather than as a smaller sample. Seed 0 is rejected: sanderling test reads it as "derive a seed from the clock", which is why conformance/gates.sh controls nothing today. Each run contributes one runs.jsonl line carrying steps to first violation by origin step, the step that armed the failed obligation, so the survival analysis never reopens a trace. Claude-Session: https://claude.ai/code/session_01A5KmftdEJ49A9z5mF5ESrX
This commit is contained in:
1 parent
9f9a01f773
commit
028534cadc
11 files changed
+1605
No files matched your search
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBuildManifest_RecordsIntendedRunsAndTemplate(t *testing.T) {
|
||||
configuration, err := parseArguments(append(baseArguments(), "--devices", "a,b", "--duration", "90s"), io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
startedAt := time.Date(2026, 8, 12, 9, 0, 0, 0, time.UTC)
|
||||
value := buildManifest(configuration, "worker-7", "/usr/local/bin/sanderling", "1.4.2", startedAt)
|
||||
|
||||
if !slices.Equal(value.Seeds, []int64{1, 2, 3}) {
|
||||
t.Errorf("seeds: got %v", value.Seeds)
|
||||
}
|
||||
if value.Host != "worker-7" || value.SanderlingVersion != "1.4.2" || value.SanderlingPath != "/usr/local/bin/sanderling" {
|
||||
t.Errorf("provenance: %+v", value)
|
||||
}
|
||||
if value.DurationMillis != 90_000 || value.MaxSteps != 300 {
|
||||
t.Errorf("budget: duration=%d max_steps=%d", value.DurationMillis, value.MaxSteps)
|
||||
}
|
||||
if !value.StartedAt.Equal(startedAt) {
|
||||
t.Errorf("started_at: got %s", value.StartedAt)
|
||||
}
|
||||
if got := argumentValue(value.ArgumentTemplate, "--seed"); got != seedPlaceholder {
|
||||
t.Errorf("template seed: got %q, want %q", got, seedPlaceholder)
|
||||
}
|
||||
if got := argumentValue(value.ArgumentTemplate, "--device"); got != devicePlaceholder {
|
||||
t.Errorf("template device: got %q, want %q", got, devicePlaceholder)
|
||||
}
|
||||
if got := argumentValue(value.ArgumentTemplate, "--output"); got != "/campaigns/a/seed-"+seedPlaceholder {
|
||||
t.Errorf("template output: got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildManifest_EmptyDeviceListSerializesAsArray(t *testing.T) {
|
||||
configuration, err := parseArguments(baseArguments(), io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
directory := t.TempDir()
|
||||
if err := writeManifest(directory, buildManifest(configuration, "host", "sanderling", "dev", time.Now())); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, err := os.ReadFile(filepath.Join(directory, manifestFileName))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var decoded struct {
|
||||
Devices []string `json:"devices"`
|
||||
Arm string `json:"arm"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &decoded); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decoded.Devices == nil || len(decoded.Devices) != 0 {
|
||||
t.Errorf("devices: got %v, want []", decoded.Devices)
|
||||
}
|
||||
if decoded.Arm != "seeded-baseline" {
|
||||
t.Errorf("arm: got %q", decoded.Arm)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user