Skip to content

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