Skip to content

Runs and evidence ​

To watch runs, use bobcat ui. Every Bobcat suite publishing on the machine, live, a page per specification, and the Event Model canvas coloured by their verdicts: that is bobcat ui, part of the free bobcat tool, on http://localhost:5525.

bash
dotnet tool install -g Bobcat.Console
bobcat ui

See The Bobcat UI for what it shows. Stoat's own /runs pages link to the same page there.

What Stoat keeps ​

Stoat still receives runs, on its own port, 5527, because coordination is built on them:

  • a test-run-gate goes done on a green run carrying its BOBCAT_RUN_TAG;
  • a slice node's evidence is the latest verdict of each spec it binds;
  • the run MCP tools (list_runs, failing_tests, await_run_completion, …) read them;
  • the plan page shows a gate's live progress while its suite runs.

None of that needs bobcat ui running. A run reaches both because BOBCAT_MONITOR_URL is a list and a Bobcat publisher sends to every URL in it. stoat run-plan, stoat gate exec, stoat gate run and stoat runner add Stoat's receiver to the list for whatever they launch. For a suite you run yourself, list both:

bash
export BOBCAT_MONITOR_URL="http://localhost:5525,http://localhost:5527"

A run checks each address once at startup (GET /api/ping, with a short timeout) and stops publishing to one that does not answer. A Stoat that is down never slows or fails a test suite. Bobcat 0.60.4 and earlier read only one URL; see Ports and wire contracts.

Exports ​

GET /api/runs/{runId}/export?format=… on 5527 (or the button on the card in bobcat ui).

Format
CTRFThe default and the richest. Retries, flaky tests and each spec's steps have their own fields.
JUnit XMLLoses detail, but GitLab, Jenkins, Azure DevOps and CircleCI read it natively.
NDJSONThe raw event stream the run published, for debugging or replay.

How long runs are kept ​

Two settings bound two different things.

Monitor:RetentionRuns (default 10)How many runs the receiver lists, per job (a repository plus a suite), so a busy repository cannot push a quiet one's runs out.
Monitor:RetentionDays (default 14)How long an archive is kept. The only setting that ever deletes a file.

A run that falls off the list is ejected: its archive moves to ejected/ and is not deleted. A live run is never ejected automatically, since its publisher's next event would bring it back. An orphaned run, whose publisher has gone, can be.

Correlating a run to work ​

BOBCAT_RUN_TAG is an opaque string Bobcat stamps on a run and never interprets — a plan node, a ticket id, a build number:

bash
BOBCAT_RUN_TAG=critter-parity/fisher-1-10 dotnet test

GET /api/runs?tag=… filters on it, and a plan's test-run-gate node reads through it, so the evidence that closes a node is the run itself.

That bare {plan}/{node} tag is for a gate that declares no steps. A gate with steps reads each step's runs under {plan}/{node}/{step}, and a run under the bare tag counts for none of them; a step whose command starts a Bobcat Gate publishes one run per unit under {plan}/{node}/{step}/{project}.{framework}. Running the steps with stoat gate run sets all of this for you. See How a step is decided.

Run evidence joins back to the model ​

A Bobcat run records the types each scenario actually touched: the aggregate it arranged, the command it dispatched, the events the stream gained, the messages sent and the read model loaded. It never records what an assertion merely names. A scenario that expects a refusal records its rejected command and no event type.

That is how a run joins back to the Event Model's slices, and what lets slice_coverage tell "never specified" apart from "specified but never exercised".

See also ​

Licensed under the Business Source License 1.1.