← Qnative (provisional) · c3

guide/06-scenarios.md

Synthetic scenarios

Standing: three worlds are bundled today. Conditions, streams and the drill are proposals.

Synthetic data is how every page is built before a sensor serves it, and how every state is tested after. The rule from the app's first day holds: synthetic data flows through the real pipes. A page never holds a fake value; it asks the same repository and gets an answer in the same shape.

A synthetic choice has three parts.

PartWhat it isChosen on the Sensor page as
Worldthe data at rest: what was found on one machine, on one daya name
Conditionwhat is going wrong between the app and its sourcenone, or one fault
Streamwhat plays on Live and Eventsa script and a speed

Worlds

A world is one file, crates/fixtures/world/<name>.json: the answers qdash's own handlers gave for every request the pages make, for one machine of the Meridian seed. scripts/cut-world.py cuts it.

WorldWhat it isStanding
meridian-day90the default; being recut to a quiet laptop (design c131 D15)bundled
minimalone harness, a few sessionsbundled
freshday 0: installed, nothing found yetbundled
pilotpriya's laptop on day 90: the machine where things happenruled, not yet cut
first-week, power-userusage at two extremesnamed, not yet cut

The page lists whatever worlds are bundled. It names none itself.

Conditions

A condition wraps the source. The page underneath does not know; it receives the error a real source would give and draws its real state.

ConditionWhat the source doesThe state it exercises
not runningno lock file"no sensor on this machine"
stalea lock file whose pid is deadFound fails; "start the monitor"
slowanswers after a delayloading, then content
refusing403, or 503 on the streamRefused, with what to change
wrong shapean answer that does not match its typea named shape error, never a blank
laggingthe stream falls behind and is cutLost; the gap is shown

Built in Phase 2 (2026-11-11): nozzle::scenario::Conditioned wraps any nozzle, in six conditions: healthy, not-running, slow (2.5 s), unavailable (REST answers, the stream gets a 503), shape-mismatch (the repository's own decode fails; the stream sends unreadable records), lag-then-cut (600 records, then cut in qdash's words, then a fresh resync). Choose one on Dev › Source or with QPOINT_CONDITION; the footer names it. The drill (c131 D15: injecting one of three bad events) is not built yet.

Streams

A stream script is recorded events in the envelope the sensor sends today (Live Activity), one JSON object per line. It is replayed through the same frame parser a live stream uses, at a chosen speed, with timestamps shifted to now.

Built in Phase 1 (2026-11-11): four generated scripts, not yet recordings. They live in crates/fixtures/src/streams.rs, written to qevents' payload shapes as read from code (../research/qevents-payloads.md). Each is seeded and deterministic, and opens with the monitor's resync (installation.snapshot, then process.snapshot):

  • quiet, 0.5 a second;
  • coding-session, 8 a second (the default);
  • mcp-heavy, 20 a second;
  • file-storm, 500 a second (the stress case).

They play through nozzle::stream::play, the same subscription the sensor's stream uses. The condition and the stream are one per process (nozzle::scenario). Until the Sensor page (Phase 2), set them with QPOINT_STREAM=<script>[@<rate>] and QPOINT_CONDITION=<healthy|not-running|slow|unavailable|shape-mismatch|lag-then-cut>. The values in the scripts are invented; a recording of a real session replaces them.

ScriptWhat it is for
quiet daythe default: a little of everything, slowly
coding sessiona harness at work: model requests, tool calls, file reads
MCP-heavysessions that lean on MCP servers
file stormfile.* at a high rate; the stress case for the buffer and for coalescing

The drill

Design c131 D15: on the pilot world a drill injects one bad event into the stream, so a person can watch what a violation looks like. Three are ruled: code pasted to an outside site, a secrets file opened, and credentials read followed by traffic to an unknown host. On the Sensor page these are the Violations family.

Recording a real session into a scenario

Today, with a sensor running, this captures its stream to a file in the script format:

curl -N http://127.0.0.1:7878/api/events/stream | sed -n 's/^data: //p' > session.jsonl

Use the URL from the lock file if the port is not 7878.

A recording is real data. It holds prompts, file paths and whatever else passed through the sensor while it ran, and installation.details can hold secrets. Never commit a raw recording. Record a session staged for the purpose, read it before it leaves your machine, and scrub what should not travel.

When Live is built, a recording can be dropped onto it to be read like a live stream, and saved from there as a script.

Adding one

  • A world: run scripts/cut-world.py against the seed for another machine or day, add the file to crates/fixtures/world/, and add its name to the bundled list. The fixtures test checks every answer against its type.
  • A stream: add the .jsonl beside the worlds. Every line must parse as the envelope.
  • A condition: one case in the wrapper, and a story for the state it produces.

Qpoint Brand Style Guide