Harness¶
Harness
| Kind | Test instrument — library + CLI |
| Project | tests/NextIteration.Harness/ |
| Exposes | Harness.Run(...), Harness.RunInChildProcess(...); the CLI; [Trait("milestone","phase-N")] tests |
| Consumes | the engine in-process, a scenario, interventions |
| Produces | per seed: final graph + full trace; summary.md, report.json, chronicle excerpts |
| Status | designed · Phase 1 (#33, #42) |
| PLAN | §22.8, §15 (Phase 1 row), §13.5 |
| CI | harness.yml (nightly, inc:* PRs), milestone.yml, pr.yml determinism job |
Emergence quality is empirical; the harness is the instrument. It runs a scenario across N seeds for M sim-days in-process — no API, no client — and returns everything the engine knows about each run.
Library¶
var runs = await Harness.Run("village", seeds: 10, days: 10,
interventions: h => h.At(day: 3, hour: 12).PlayerAction(Ignite("inn")));
foreach (var r in runs) { r.Seed; r.Graph; r.Trace; r.Report; }
Interventions are queued through the ordinary player-action queue, so a harness-lit fire is a player:harness action in the trace and Explain ends at it. The trace buffer is sized to the run.
CLI and report¶
dotnet run --project tests/NextIteration.Harness -- village --seeds 10 --days 10 --out out/
dotnet run --project tests/NextIteration.Harness -- village --seeds 1 --days 10 --golden --processes 2
summary.md (also the CI step summary), report.json (per seed + aggregate), chronicle/seed-N.md (excerpts around interventions). Fields: survivors, deaths by cause, fires started/contained, option histogram by action and by need, bounds hits, tick duration p50/p95. --golden runs one seed in two child processes and diffs per-tick digests; a mismatch names the tick.
Milestones are tests, not probes¶
A phase's story milestone is a set of xUnit tests in this project tagged [Trait("milestone", "phase-N")]; milestone.yml runs dotnet test --filter milestone=phase-N. Assertions are LINQ over the trace's History, Decisions and Events plus graph queries on the final snapshot. No probe registry, no profile JSON, no assertion mini-language.
The Phase 1 assertions (authoritative text in PLAN §15): survival 50/50 in 10/10 seeds; campfire gathering on some night in ≥ 8/10 (≥ 30% of outdoor NPCs contained by fire_pit for a continuous sim-hour); flee-vs-fight split by personality_caution ≥ 8/10; bucket race (intent_conflict on a Vessel) ≥ 8/10; Kira's first move after the fire explained to the player action within 6 links.
Tuning levers are named¶
A story assertion can fail on demographics rather than design: the bucket race needs brave NPCs near the river at ignition, which with five river NPCs drawn from caution 0.2–0.8 is roughly a coin flip per seed. The levers are the intervention time and the scenario's starting placement — never the thresholds.
History¶
| Phase | Change |
|---|---|
| 1 | Introduced as a library + CLI; milestones as trace-based tests. |
See also¶
Causal trace · Determinism · CLI · phases/phase-01-foundations.md