deterministic=True execution mode to the Forge runtime that pinsLD_PRELOAD, kernel build, GPU driver, andenv_hash such that two replays of the same analysisforge/runtime.py (632 LoC) and scidex/forge/executor.py (898pip upgrade silently breaks reproducibility. Deterministic modeRuntimeSpec (in forge/runtime.py) gains a deterministic: boolpip freeze --all and writes requirements.lock into thePYTHONHASHSEED=0, OMP_NUM_THREADS=1, OPENBLAS_NUM_THREADS=1,SCIDEX_RNG_SEED._det_preamble.py that seedsrandom.seed, numpy.random.seed, torch.manual_seed,torch.cuda.manual_seed_all, jax.random.PRNGKey.
RuntimeResult records env_hash = sha256(requirements.lock +repro_capsule_schema.json extended with env_hash,requirements_lock_path, seed.
scripts/replay_analysis.py <analysis_id> re-runs the analysis withenv_hash cannot be reproduced, anddeterministic=True 1 hour apart, assert byte-identical outputs.env_hash is order-independent: sort lines before hashing.forge/runtime.py, scidex/forge/executor.py, repro_capsule_schema.json.All acceptance criteria implemented:
RuntimeSpec.deterministic: bool field added to forge/runtime.pyrun_python_script(deterministic=True) calls _setup_deterministic_environment to:pip freeze --all and write a sorted requirements.lock.<seed>.txtPYTHONHASHSEED=0, OMP_NUM_THREADS=1, OPENBLAS_NUM_THREADS=1, SCIDEX_RNG_SEEDrandom, numpy, torch, jax, and scikit-learn before exec'ing the user script
RuntimeResult gains env_hash, requirements_lock_path, seed fieldsenv_hash = sha256(kernel + glibc + cuda + locale + tzdata) — order-independent, sorted keysrepro_capsule_schema.json extended with env_hash (pattern ^sha256:[a-f0-9]{64}$), requirements_lock_path, seedscripts/replay_analysis.py added: loads capsule metadata, verifies env_hash, re-runs with seeded wrapper, diffs output file hashes byte-by-byte, exits 0 only on bit-identical replay_write_det_wrapper call (was writing to system temp before tmpdir existed); sys.argv now injected into wrapper so args pass through correctly; __name__ fixed in exec namespace so if __name__ == '__main__': guards fire correctlyrandom.random() output