Iterate, Versions & Reports¶
A study that has already run is a result — iteration must never silently destroy it. SocioVerse enforces that with one rule and one mechanism.
The rule: all changes go through /sv-iterate¶
Any change to an existing study — a new policy broadcast, more steps, a
different population size, a tweaked threshold — goes through /sv-iterate.
Don't hand-edit and re-run
Editing artifacts in place and re-running overwrites the previous
trajectory with no record. "add one more policy and run again" is an
iteration, not an edit.
Before the gate: the reference guard¶
A study whose study.yaml carries reference: true — the bundled demo
templates and adapted baselines — is fork-only. This guard runs before
the version gate and bypasses it entirely: new-version, in-place and branch all
mutate the same study_id, which is exactly what a shared baseline must never
suffer.
So a "modify this" request against a reference study forks it:
- the fork gets a new
study_id(andreference: false); the template is untouched; - a fresh fork has no history to version, so the version gate is skipped —
it starts its own version line at
v1and goes straight into the pipeline.
Editing the baseline template in place is a rare, deliberate choice that requires an explicit, spelled-out warning first: every future study built from that template inherits the change. A non-reference working study skips this guard and goes straight to the gate.
The version gate¶
/sv-iterate always asks (never assumes) how to version the change:
| option | what happens |
|---|---|
| new version (recommended default) | the live directory is snapshotted to versions/v<N>/ — artifacts, code, results — then the study iterates as v<N+1> |
| in-place | no snapshot; the current outputs are lost (the classic overwrite flow, opt-in only) |
branch from v<X> |
the new version starts from an older version's artifacts; versions.json records the parent tree, so cross-version exploration stays navigable |
Snapshots are immutable local history (versions/ is gitignored, like run
outputs): old versions never auto-delete, and anything that ever ran stays
inspectable.
Mode and provider switches are new-version-only
A switch of decision/run mode or provider — scripted ↔ LLM, local ↔ real-LLM, deterministic ↔ stochastic — is not offered the in-place option at all. The earlier run is the scientific comparison baseline: the whole point of a scripted dry-run or a local-model pass is to sit next to the real one. Only new version and branch are offered.
The review gate (待复核)¶
Creating a version requires saying where the pipeline resumes.
create_version(..., resume_from_stage=...) takes:
| change | resume_from_stage |
|---|---|
| new version from current | the earliest affected stage — a new broadcast → sv-build-environment; a population change → sv-build-population; a decision-logic change → sv-build-model |
| branch from an older version | sv-init — branching is a large context shift, so everything is re-reviewed |
From that stage forward, every carried stage shows as 待复核 / pending review in the dashboard until you handle it. There are exactly two ways to handle one:
- Affected stage → re-author it through its
sv-*skill, as usual. - Unaffected stage → validate the carried artifact against its schema, confirm it still fits the new question, and post its narrative. That narrative is what clears the 待复核 flag.
Verify and narrate — do not rewrite an unaffected artifact
Rewriting a carried artifact "just to be safe" churns its mtime and corrupts the dashboard's carried-vs-stale detection. Validate it, say why it still holds, and leave the bytes alone.
A carried stage that is neither re-authored nor verified stays 待复核 and
holds the pointer — so the pipeline can never look finished while an
unreviewed stage rides along from the parent version. /sv-run still confirms
before spending.
Warm starts¶
When the change only affects the trajectory from step J onward — the horizon was extended, or a new intervention lands at step J — a full re-run would re-pay LLM cost for steps that cannot differ. A warm start avoids that:
/sv-run then replays the parent's steps 0..K from its stored actions —
reproduced exactly, no LLM — and spends only on steps K+1..n_steps.
Eligibility
Warm starts apply to from-scratch (Path B) studies with
interaction_rounds == 1, whose apply is a pure function of the
actions. Wrapped-legacy and multi-round studies re-run from step 0; the
engine raises an error rather than warm-starting them incorrectly.
The multi-round exclusion is not a limit of the replay code but of what was stored: only the final round's actions are persisted in the parent panel, so the intermediate rounds that produced them cannot be reconstructed. See Multi-round interaction.
Reports¶
/sv-report reads trajectory/study.duckdb (panel, metrics, events,
optionally messages) and writes reports/report.md plus
reports/figures/*.png. The report is analytical, not a data dump, and its
structure is mandated — a 总–分–总 arc, in this order:
| # | section | what it must do |
|---|---|---|
| ① | 总起 · 结论速览 (Overview) | the 2–4 headline conclusions of the whole run, one line each, stated as claims — "X happened because Y", never "we simulated X". This is the thesis the body then proves. |
| ② | trajectory tables & figures | the descriptive backbone everything else reads off |
| ③ | 分 · 发现与解读 (Findings) | roughly one finding per key result. Each claim is immediately paired with the specific number or figure it reads off, and with the mechanism — the why, read off the panel and cohorts. |
| ④ | 深层洞察 (Deeper insights) | non-obvious cross-metric / cross-cohort conclusions the raw tables don't state — who is insulated vs exposed, a tipping threshold, a divergence between two metrics — each still tied to the data that reveals it |
| ⑤ | 总结 · 回扣与深挖 (Conclusion) | restate the headlines, now earned by the body, and go one level deeper: what they mean for the research question, the unifying mechanism, what remains uncertain. No new unsupported claims. |
| ⑥ | 改进建议与下一步 | actionable suggestions, and what the next /sv-iterate round should test — a policy to try, a parameter to sweep, a cohort to probe |
| ⑦ | 数据基础与参考来源 | last, rendered from the grounding sidecar |
The binding rule of section ③
No claim without its number, no number without the claim it supports.
A finding reads like "满意度从 0.74 跌到 0.61(step 2 政策后,见
figures/satisfaction.png)" followed by the mechanism — not a paragraph of
interpretation next to a table the reader has to reconcile on their own.
The last section is generated from grounding/grounding.json as a pipe table:
| 事实 | 值 | 依据 | 来源 |
|---|---|---|---|
| … | … | 实证 / 代理 / 假设 | title (url) |
plus implementation_refs as 建模参考 and assumptions as 声明的假设. A study
whose method_notes is "stylized" gets the single honest line
"风格化模型,无真实世界锚点。"; a forked study opens the section by saying which
anchors it inherited. See
Grounding & Provenance.
Ask your own questions with SQL¶
The report is a rendering; the store is the result. study.duckdb stays
queryable forever:
-- the longitudinal view of one agent
SELECT step, state, action_kind
FROM panel WHERE agent_id = '…' ORDER BY step;
-- the aggregate trajectory
SELECT * FROM metrics ORDER BY step;
-- what fired, and when
SELECT step, note FROM events ORDER BY step;
-- if the study used the message bus: who said what
SELECT step, author_id, content FROM messages ORDER BY step;
Parquet exports (panel.parquet, metrics.parquet) are written alongside at
finalize time for pandas/R workflows. More query recipes live in the
FAQ.