Drive the real simulation headlessly from a Colab notebook - #48
Drive the real simulation headlessly from a Colab notebook#48charlieijk wants to merge 3 commits into
Conversation
Strata's authoritative simulation is a stdlib-only Go package, so it runs anywhere a Go toolchain does -- including a headless Colab VM, where the browser client and the Wails desktop host cannot run at all. This adds the harness and the notebook that exploit that. `cmd/strata-simtrace` advances `sim.Step()` a fixed number of times and writes one CSV or JSONL record per tick straight off `sim.Snapshot`, with an optional per-tick sha256 over the whole snapshot (mirroring the replay contract's authoritative hash, so agreement covers every agent position, energy and overlay cell rather than the summary metrics alone). It decides nothing itself. It rejects seed 0, which `sim.Reset` reads as a wall-clock seed, and an unknown preset name, which the engine would otherwise answer silently as `focus-agent`. `notebooks/01_simulation_dynamics.ipynb` installs the Go toolchain that `go.mod` pins -- read from the file, asserted, never hardcoded -- clones via a Colab secret and strips the token from the remote, then measures two things on 10 seeds of the `ecosystem` preset: - Determinism: a 2500-tick trace is byte-identical across two processes at seed 101 (sha256 e10be14e...), all 2500 per-tick snapshot hashes match, and a 1000-tick run is a prefix of the 2500-tick one. Against seed 202 every one of the 2500 ticks differs, first difference on tick 1, no hash collisions -- while populations stay within 24 agents. The seed decides which agent stands where, not how many the world carries. - Dynamics: 1000 agents overshoot to 1004-1025, crash through half the starting count at tick 1672 +/- 12, then hold a plateau of 241-288 agents (CV 6.1%) with a fitted tail slope under 0.1 agents per 1000 ticks and no extinction in any seed. Mean energy per survivor climbs from ~85 to 2202-2787: nothing caps it, and reproduction is gated on crowding and stress as well as energy. Results carry a provenance stamp and land in a git-ignored `notebooks/results/`; a committed measurement quietly becomes a stale claim the next time the preset is tuned.
|
Warning Review limit reached
Next review available in: 6 minutes Limit details: You’ve used all 10 included reviews currently available. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
Comment |
The dynamics section claimed births roughly match deaths on the plateau, but the only counters in the table were cumulative and therefore dominated by the crash that precedes it -- 99-418 births against 858-1130 deaths says nothing about whether the tail is held or slowly draining. Difference the counters across the tail window instead. Across the ten seeds the plateau runs 1198 births against 1181 deaths, a 1.4% imbalance, which is what makes it an equilibrium rather than a slow decay. Also corrects the prose on the fitted tail slope: it averages +0.09 agents per 1000 ticks, but one seed reaches 2.31, so "a fraction of an agent" was true of the mean and not of the sample.
CI's golangci-lint run flagged the unchecked fmt.Fprintln (errcheck). The metadata line carries the run's identity -- seed, preset, toolchain -- so a silent failure to write it would leave a trace with no provenance attached, which is exactly the failure the notebook's stamp exists to prevent. Return the error.
Strata's authoritative simulation is a stdlib-only Go package, so it runs anywhere a Go toolchain does — including a headless Colab VM. This adds the harness and the notebook that exploit that.
What cannot run on Colab, stated plainly
web/, Vite + three.js) needs WebGL, a canvas and a display. Not present on a headless Ubuntu runtime.cmd/strata-desktop/) needs native WebKit/GTK and a windowing system. It is a separate Go module precisely so the server and simulation never drag that toolchain in.Out of scope here; their gates stay
npm --prefix web testandmake desktop. The world manifest and the.glb/.ktx2/.oggassets are authored presentation data, not simulation inputs — the manifest contributes world dimensions and nothing else to these runs.cmd/strata-simtraceA thin driver: construct a
sim.Simulation, callStep()N times, write one CSV or JSONL record per tick straight offsim.Snapshot. It decides nothing itself.authoritativeSnapshotHash. Agreement therefore covers every agent position, energy, credit and cargo value plus the pressure/scar overlays — not just summary metrics.0, whichsim.Resetreads as a wall-clock seed, so an unrepeatable run cannot masquerade as evidence.focus-agent.'g', 17), so a value read in Python is the value the engine computed.Tests cover the guards, the CSV/JSONL shapes, the hash stride, byte-identical repeat runs, the prefix property, and float round-tripping. Each was mutation-checked: breaking the stride check, the seed-0 guard, the preset guard, or float precision fails the corresponding test.
notebooks/01_simulation_dynamics.ipynbFollows the fight-lab / Orbit notebook convention. Reads the Go version out of
go.mod(toolchain pin asserted exactly, never hardcoded), clones via theGITHUB_TOKENColab secret and strips the token from the remote, then asserts no credential remains.Determinism, measured
sha256:e10be14e…)2500 ticks is chosen to run past the population crash below, so the proof covers the most numerically violent stretch rather than a quiet opening.
The two levels disagree, which is the interesting part: every tick's state differs between seeds, yet populations stay within a couple of dozen agents. The seed decides which agent stands where; it barely decides how many the world carries.
Dynamics:
ecosystempreset, 10 seeds × 12000 ticks (600 s simulated)Seeds 101, 202, 303, 404, 505, 606, 707, 808, 909, 1010.
Limits (in the notebook, not just here)
Browser and desktop surfaces out of scope; assets are presentation data; the expedition/game path is never entered, so terrain, hazard and cargo modifiers are inert in these traces; 10 seeds is a small sample; one preset at shipped parameters; determinism proved within one toolchain and architecture only; 600 s is inside
MaxAge(900 s) so nothing here says what happens once the founding cohort ages out.Housekeeping
notebooks/results/git-ignored — a committed measurement quietly becomes a stale claim the next time the preset is tuned. Saved results carry commit, dirty flag, toolchain, platform and timestamp.notebooks/README.mdadapted from the siblings: Colab open URL, multi-account/authuserwarning, save-to-branch, clear-outputs, ground rules.Verification
make gofmt-check go-vet go-test manifest-check determinism-check evalgreen;make go-coverat 82.6% against the 78% floor. Every notebook code cell was executed locally end to end — the numbers above are that run's output, not estimates.