Skip to content

Seeded mix over assoc-shaped data is not reproducible across processes (rand itself is) #607

Description

@domg123

Version: 81.1.1 and 81.1.2 (native CLI, macOS arm64; also observed on the WASM build of 81.1.1)

After set_rand_seed, the seeded stream itself is cross-process stable — (rand) sequences repeat exactly. But mix over identically-constructed assoc literals produces different output in different processes, for flat assocs of ~4+ keys and nested assocs alike:

  • sorted-key canonicalization of the inputs does not help;
  • recursive_matching .false does not help;
  • list-shaped inputs do not show the effect;
  • within one process results ARE stable (repeated runs in the same process match).

Inferred mechanism: mix's internal traversal of assoc pairs follows a pointer/ASLR-sensitive order, so the seeded stream is consumed in a different order per process. The same effect presumably underlies mix_entities reproducibility reports, but this shows it at the plain code-level mix opcode — anyone moving from string/list-shaped genomes (which replay fine) to assoc-shaped data inherits it silently.

Repro — run this file several times as separate processes and compare (the rand line is byte-identical every run; both mix lines differ run to run; in our testing 6 runs produced 6 distinct flat-assoc outputs):

(seq
  (set_rand_seed "repro-rand")
  (print "rand (stable):      " (rand) " " (rand) "\n")

  (set_rand_seed "repro-mix-flat")
  (print "mix flat assoc:     "
    (format (mix
      {a 1 b 3 c 5 d 7 e 9 f 11 g 13 h 15}
      {a 2 b 4 c 6 d 8 e 10 f 12 g 14 h 16}
      0.5 0.5 {nominal_numbers .false similar_mix_chance 0.5})
      "code" "json" .null {sort_keys .true}) "\n")

  (set_rand_seed "repro-mix-nested")
  (print "mix nested assoc:   "
    (format (mix
      {parts [{part "body" pr 0.9 color {r 10 g 20 b 30}}] traits {speed 0.2 kind "wanderer"}}
      {parts [{part "body" pr 1.1 color {r 240 g 220 b 200}}] traits {speed 0.6 kind "hunter"}}
      0.5 0.5 {nominal_numbers .false similar_mix_chance 0.5})
      "code" "json" .null {sort_keys .true}) "\n")
)

Expected: same seed + same literal inputs → same mix output across processes.
Observed: different output almost every process.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions