[Testing] EngineRandom stream decorrelation is claimed in a comment and gated nowhere #409

Open
opened 2026-07-27 15:52:46 +00:00 by john · 0 comments
Owner

Found during review of #394 (commit ac29b35 on sim/integration). Reported by the owner as a
declared mutation survivor; reproduced independently by the reviewer.

src/EngineRandom.cpp derives each subsystem stream from the master seed with
std::seed_seq seq{ s.master, static_cast<uint32_t>(i) }. Replacing that with
std::seed_seq seq{ s.master } makes every stream emit the identical raw mt19937 sequence, and
all three of #394's test files pass green with zero failed assertions:

R9_M6_streams_seeded_identically   SURVIVES   unit=0/0  audio=0/0  integ=0/0

What it is not. Not a replay or determinism defect. The streams remain independent generator
objects
, so a draw from AUDIO still does not advance BEHAVIOR; every replay, divergence and
stream-independence property in #394 continues to hold. src/EngineRandom.h's stated rationale for
independent streams — that an audio-on and an audio-off run at the same seed produce the same world —
is unaffected.

What it is. Entity movement and audio variation become perfectly correlated: the same underlying
numbers drive both. That is a statistical defect, and the header comment asserting decorrelation
("std::seed_seq mixes, so adjacent masters do not produce correlated streams") is currently an
unverified claim.

Why no gate exists today, and what one would have to look like. There is no deterministic
Python-observable relating two streams: BEHAVIOR is consumed through uniform_int_distribution and
AUDIO through uniform_real_distribution, and nothing else consumes either. Relating a move choice
to a pitch offset would mean encoding libstdc++'s distribution internals, which is a worse asset than
an ungated line.

A distribution-free dependence test is constructible and would not encode internals: over a few
hundred seeds, record entity 0's first move index and the first drawn pitch, bin the pitch into as
many bins as there are candidate moves, and chi-square the two. Under the current implementation the
two are independent by construction; under the single-argument seed_seq they are strongly
dependent (both are dominated by the same first raw output). It is a statistical test with a flake
surface, which is why it is filed rather than required.

Deferred deliberately. Recorded so that "16 of 17 mutations caught" is not later read as "every
property of EngineRandom is gated."

Found during review of #394 (commit `ac29b35` on `sim/integration`). Reported by the owner as a declared mutation survivor; reproduced independently by the reviewer. `src/EngineRandom.cpp` derives each subsystem stream from the master seed with `std::seed_seq seq{ s.master, static_cast<uint32_t>(i) }`. Replacing that with `std::seed_seq seq{ s.master }` makes **every stream emit the identical raw `mt19937` sequence**, and all three of #394's test files pass green with zero failed assertions: ``` R9_M6_streams_seeded_identically SURVIVES unit=0/0 audio=0/0 integ=0/0 ``` **What it is not.** Not a replay or determinism defect. The streams remain independent *generator objects*, so a draw from AUDIO still does not advance BEHAVIOR; every replay, divergence and stream-independence property in #394 continues to hold. `src/EngineRandom.h`'s stated rationale for independent streams — that an audio-on and an audio-off run at the same seed produce the same world — is unaffected. **What it is.** Entity movement and audio variation become perfectly correlated: the same underlying numbers drive both. That is a statistical defect, and the header comment asserting decorrelation ("`std::seed_seq` mixes, so adjacent masters do not produce correlated streams") is currently an unverified claim. **Why no gate exists today, and what one would have to look like.** There is no *deterministic* Python-observable relating two streams: BEHAVIOR is consumed through `uniform_int_distribution` and AUDIO through `uniform_real_distribution`, and nothing else consumes either. Relating a move choice to a pitch offset would mean encoding libstdc++'s distribution internals, which is a worse asset than an ungated line. A **distribution-free dependence test** is constructible and would not encode internals: over a few hundred seeds, record entity 0's first move index and the first drawn pitch, bin the pitch into as many bins as there are candidate moves, and chi-square the two. Under the current implementation the two are independent by construction; under the single-argument `seed_seq` they are strongly dependent (both are dominated by the same first raw output). It is a statistical test with a flake surface, which is why it is filed rather than required. Deferred deliberately. Recorded so that "16 of 17 mutations caught" is not later read as "every property of `EngineRandom` is gated."
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
john/McRogueFace#409
No description provided.