sim: hostlife: deterministic purge drain; mutate.sh witness log path
The purge drain iterated the app's registrations in Go map order, so a seed with two registrations at a drain could differ from run to run, and the same binary gave 49,743, 49,744 and 49,745 completed operations over 30,000 seeds. It now drains in create-id order; two runs agree exactly (49,732). This was the only behaviour-affecting map iteration; the rev 7.2 counts were subject to it and are rerun for rev 7.3. mutate.sh wrote the witness log under an unset variable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+15
−3
internal/sim/hostlife/console_test.go+14 −2internal/sim/hostlife/mutate.sh+1 −1
diff --git a/internal/sim/hostlife/console_test.go b/internal/sim/hostlife/console_test.go| index d50aca6c7..78167bcb9 100644 |
| --- a/internal/sim/hostlife/console_test.go |
| +++ b/internal/sim/hostlife/console_test.go |
| @@ -1,6 +1,9 @@ |
| package hostlife |
| -import "fmt" |
| +import ( |
| + "fmt" |
| + "sort" |
| +) |
| // consolePlan is a multi-step console operation. A console crash drops |
| // every plan; the claim-recovery pass must then finish or undo the work |
| @@ -627,7 +630,16 @@ func (w *world) purgeSteps(p *consolePlan) bool { |
| // drain: every registered create reaches serving or is cancelled |
| // before the host set is fixed; an expired registration is dead |
| if on("admission-registration") { |
| - for id, e := range ad.Reg { |
| + // in create-id order: Go's map order would make the drain, and |
| + // with it every seed after a purge, differ from run to run |
| + // (found in rev 7.3; the sole source of nondeterminism) |
| + ids := make([]string, 0, len(ad.Reg)) |
| + for id := range ad.Reg { |
| + ids = append(ids, id) |
| + } |
| + sort.Strings(ids) |
| + for _, id := range ids { |
| + e := ad.Reg[id] |
| // an expired registration whose host's claim does not name |
| // its create is dead: the create checks the expiry right |
| // before its claim CAS, so it can no longer claim (CC16) |
diff --git a/internal/sim/hostlife/mutate.sh b/internal/sim/hostlife/mutate.sh| index eb64cd513..57a80bb71 100755 |
| --- a/internal/sim/hostlife/mutate.sh |
| +++ b/internal/sim/hostlife/mutate.sh |
| @@ -91,7 +91,7 @@ one() { |
| # invariant, "held" if they all hold, "none" if the rule has no witness. |
| # A witness that must violate under this mutation and holds fails the |
| # test, which is an ERROR here: the witness is broken, not the rule |
| - wlog="$SCR/logs/$tag.witness.log" |
| + wlog="$SCRATCH/logs/$tag.witness.log" |
| HOSTLIFE_MUTATE=$r "$BIN" -test.run '^TestWitnesses$' -test.count=1 -test.v -test.timeout 10m >"$wlog" 2>&1 |
| wrc=$? |
| wit=none |