Summary
Add a deterministic execution mode so a given source + config + host produces repeatable output: a host-injectable frozen clock and a seeded portable PRNG.
Why
Reproducible runs are core to the sandbox-for-AI vision (reproducible agent runs and reproducible test262), but every non-deterministic source reads a global OS/FPC primitive directly with no injection seam: Math.random calls FPC Random (Goccia.Builtins.Math.pas:479), the clock is TimingUtils.GetEpochNanoseconds / GetNanoseconds (raw clock_gettime / QueryPerformanceCounter), and Temporal.Now / Date.now / performance.now read those. Object/Map/Set iteration order is already deterministic, so no change is needed there.
Current behavior
Math.random -> FPC Random, never seeded, no seam.
- Wall clock
GetEpochNanoseconds feeds Temporal.Now and (via shim) Date.now / new Date(); monotonic GetNanoseconds feeds performance.now.
- Nothing accepts a seed or injectable clock. (
GocciaBenchmarkRunner --profile-deterministic is unrelated — a naming-collision risk.)
Expected behavior
Decided (grill): an engine-wide --deterministic CLI option (plus config key) that:
- Injects a single synthetic clock at the
TimingUtils seam, frozen at a fixed epoch — making Temporal.Now and Date deterministic together (shared GetEpochNanoseconds); performance.now (separate GetNanoseconds) is frozen the same way and timeOrigin is pinned.
- Seeds a portable PRNG (e.g. splitmix64 / PCG, not FPC
Random) for Math.random, so output is stable across platforms and FPC versions.
- Pins the system timezone to UTC (
Temporal.Now.timeZoneId / Date read host TZ today).
Scope notes
Summary
Add a deterministic execution mode so a given source + config + host produces repeatable output: a host-injectable frozen clock and a seeded portable PRNG.
Why
Reproducible runs are core to the sandbox-for-AI vision (reproducible agent runs and reproducible test262), but every non-deterministic source reads a global OS/FPC primitive directly with no injection seam:
Math.randomcalls FPCRandom(Goccia.Builtins.Math.pas:479), the clock isTimingUtils.GetEpochNanoseconds/GetNanoseconds(rawclock_gettime/QueryPerformanceCounter), andTemporal.Now/Date.now/performance.nowread those. Object/Map/Set iteration order is already deterministic, so no change is needed there.Current behavior
Math.random-> FPCRandom, never seeded, no seam.GetEpochNanosecondsfeeds Temporal.Now and (via shim)Date.now/new Date(); monotonicGetNanosecondsfeedsperformance.now.GocciaBenchmarkRunner --profile-deterministicis unrelated — a naming-collision risk.)Expected behavior
Decided (grill): an engine-wide
--deterministicCLI option (plus config key) that:TimingUtilsseam, frozen at a fixed epoch — making Temporal.Now and Date deterministic together (sharedGetEpochNanoseconds);performance.now(separateGetNanoseconds) is frozen the same way andtimeOriginis pinned.Random) forMath.random, so output is stable across platforms and FPC versions.Temporal.Now.timeZoneId/ Date read host TZ today).Scope notes
TimingUtils.GetEpochNanoseconds+GetNanoseconds(both insource/shared, used by runtime and tooling). Avoid the--profile-deterministicname collision.