recon_gen.common.spine.rng
Deterministic RNG factory for ViolationGenerator impls.
Every generator that makes a non-trivial choice — anomaly’s 20 quiet baseline pairs (which sender/recipient accounts, which amounts within a quiet range), money_trail’s chain of N hops (parent-linked transfer IDs), AS.4’s cross-account vector state (opening balances across many accounts) — needs randomness. Naive random.X() calls would break byte-identical replay across runs.
The convention this module locks in (matching the rest of the codebase — build_full_seed_sql(base_seed=…), random.Random(seed=…)):
Each concrete ViolationGenerator that makes a random choice carries an rng: random.Random field on the dataclass.
Each Invariant.scenario_for(…) accepts a seed: int | None = None kwarg; None resolves to SCENARIO_BASE_SEED (this module’s default).
The generator constructs its RNG via scenario_rng(seed) — never calls random.Random() directly.
The tests/unit/test_typing_smells.py::determinism AST lint already rejects module-level random.X() calls in seed modules; this convention extends it to spine generators by funneling every construction site through one factory.
Composing multiple generators in one scenario: each gets its OWN rng, seeded from a derived value so the order of generator construction doesn’t affect the rng stream a downstream generator sees. The recommended derivation is seed + i where i is the generator’s position in the scenario. Locking the precedent here so AS.2 + AT don’t have to re-litigate.
Module Attributes
Default seed for generators when the caller doesn't pin one. |
Functions
|
Build a seeded random.Random for a ViolationGenerator. |
- recon_gen.common.spine.rng.SCENARIO_BASE_SEED: Final[int] = 23053819629
Default seed for generators when the caller doesn’t pin one. Hex-spelled “spine seed” — uncommitted to a specific calendar date so swapping locked anchors doesn’t drag this with it.
- recon_gen.common.spine.rng.scenario_rng(seed=None)[source]
Build a seeded random.Random for a ViolationGenerator.
seed=None uses SCENARIO_BASE_SEED — every scenario_for call site that doesn’t pin its own seed lands at the same starting point, so the locked-seed agreement (tests/data/test_locked_seeds.py shape) keeps holding.
- Return type:
Random- Parameters:
seed (int | None)