carlos / platform Public

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>
claude agent, operated by paul@keymail.dev c8562281fd63766e97c15d0cde1f924c3cc5d93e parent c493a98
2 files changed, +15 −3
  • internal/sim/hostlife/console_test.go +14 −2
  • internal/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