Playtesting a living narrative

A living-world playtest is not a reading for prose quality. It is an experiment in whether people and autonomous characters can perceive, choose, act, recover, and leave a coherent world behind.

Start from a known snapshot

Save a baseline before each test run. Record the enabled plugins, world seed, model/provider profiles, controller assignments, world time scale, and any external scripts. A snapshot lets you replay the same starting conditions across human, scripted, behavior-tree, and LLM controllers.

Do not compare two runs that began with different hunger, item custody, open obligations, or world epochs and call the difference a controller result.

Test in layers

Static comprehension

Ask a new participant to identify:

No quest action is needed yet. If the place is confusing while static, autonomy will amplify the confusion.

Interactive causality

Exercise every required transition and its failure paths:

Verify actual components, edges, events, and projections rather than trusting narration.

Living behavior

Run bounded sessions with each autonomous character. Observe whether goals, needs, relationships, routines, and memory influence different choices. Include interruptions:

A good world supports recovery rather than only the happy path.

Observe through several surfaces

Use the graph inspector to examine map, region, social, and quest structures. Turn on the event feed to correlate actions with state changes. Use a player client to see only what the character sees. Use the character-memory admin surface to audit private recall without exposing it to players.

For LLM diagnosis, traces can show prompt size, recall-filter application, retrieved memory count, provider attempt, tool call, and validated result. Keep full prompt content capture off unless a controlled privacy review requires it.

The concurrent LLM player harness is such a controlled admin surface. Its --trace-output NDJSON file flushes after each turn and attributes the configured system prompt, exact provider request/history, all response attempts, decoded tool call, and command result to a player and character. Monitor it while the run is active with tail -f:

scripts/run-multiplayer-llm examples/playtests/multiplayer-llm.yml \
  --output artifacts/playtests/multiplayer-llm.json \
  --trace-output artifacts/playtests/multiplayer-llm.trace.ndjson
tail -f artifacts/playtests/multiplayer-llm.trace.ndjson

For the release-scale ten-player exercise, use scripts/prepare-multiplayer-fixture --output artifacts/playtests/shared-10-fixture and examples/playtests/multiplayer-llm-10.yml. The generated world and short-lived player tokens are intended for a loopback server only; do not target the hosted soak or commit the fixture directory and sensitive trace.

The result JSON and NDJSON are operator-only evidence. They contain full prompts, responses, and—unless log_thinking: false is configured—provider thinking. Keep both outside public logs and tickets and apply the playtest evidence retention policy. Authentication credentials and claim secrets are deliberately excluded.

Server logs and telemetry are for internal errors. Character prompts should contain only conditions they can address in the world.

Score milestones, not preferred prose

Define authoritative milestones before the run:

MilestoneEvidence
Found the supply conflictinspected custody and spoke or acted on it
Restored the lanterntarget state shows repaired and lit
Reopened the crossingroute or ferry state changed
Delivered medicineitem custody and receipt/delivery event agree
Settled the old promiseobligation status and relationship consequence persisted
Learned the ledger factrelevant character memory or repeatable knowledge source exists

Do not require a specific route unless the test is explicitly about that route. Record which solution emerged and where the world failed to communicate alternatives.

Audit world health over time

Persistent simulations reveal problems that short tests miss. Compare snapshots after dozens or hundreds of ticks:

Growth is not automatically a leak: memories, history, births, crafted items, and player writing can be intentional. Every growing class should have a narrative reason, a retention rule, and a bounded or observable lifecycle.

Use failures to revise the world

Classify failure before changing anything:

FailureTypical revision
Did not perceive the destinationadd persistent signs, names, or route knowledge
Understood but lacked an actionadd the correct component, plugin, or handler
Acted but state did not changerepair handler or consequence ownership
State changed but nobody knewimprove event visibility or persistent evidence
Repeated a completed taskresolve or replace stale goals and obligations
Chased false memoryexpose current state and preserve memory provenance
Model alone failedcompare models only after checking world clarity and tool contract

Avoid solving every failure with stronger prompt instructions. The best fix usually helps human players, terminal clients, and other models at the same time.

Release checklist

With those checks passing, the world is ready to grow. Continue with Part IV, Expanding an existing world.