# Read a run's results

Every run prints a summary and seals a record under .testkit/results. This page shows how to read both.

## Read the summary

```console $ npx testkit run
[1/6] PASS api ui src/web/__testkit__/checkout.ui.testkit.ts 2s
[2/6] PASS api contract src/checkout/__testkit__/refund.contract.testkit.ts 0s
[3/6] PASS api contract src/__testkit__/health.contract.testkit.ts 0s
[4/6] PASS api int src/account/__testkit__/profile.int.testkit.ts 0s
[5/6] PASS api dal src/db/__testkit__/refunds.dal.testkit.ts 0s
[6/6] PASS api int src/checkout/__testkit__/health.int.testkit.ts 0s

Summary
Result: PASSED
Passed: 6
Failed: 0
Skipped: 0
Not run: 0
Files: 6
Duration: 5s
Run ID: 1791891435785-1572397-5c629188
```

A file line gives the file's position, its result, its service, its test type, its path, and how long it took.

| Line | What it counts |
| --- | --- |
| `Passed`, `Failed` | Files that ran |
| `Skipped` | Files that a skip rule held back |
| `Not run` | Files that the run selected and did not run |
| `Files` | Every file the run selected |
| `Runtime errors` | Files that failed outside their tests, such as in a setup module. The line appears only when the count is above zero. |
| `Run ID` | The name of the run's directory under `.testkit/results/runs/` |

The run prints one line per file and a short block per failure. Service logs and engine output go to disk.

## Find a run

```console $ npx testkit artifacts --list
1791891435785-1572397-5c629188 passed 2026-10-13T11:37:20.929Z
1791891435201-1572245-010ad857 passed 2026-10-13T11:37:15.203Z
1791891434722-1572097-fbd9051c failed 2026-10-13T11:37:14.724Z
```

The list is newest first. Every other `artifacts` command reads the latest run unless you pass `--run <id>`.

## See what a file left behind

Pass a test file to list its artifacts. Each entry has a name, a kind in brackets, a summary, and a path.

```console $ npx testkit artifacts src/checkout/__testkit__/refund.contract.testkit.ts
testkit-vitest-report [engine.testkit-vitest-report]
  Testkit native Vitest report
  .testkit/results/runs/1791891435785-1572397-5c629188/files/artifacts/api/task-4-01-testkit-vitest-report.json
task-engine-native [engine.task.native]
  task engine-native evidence
  .testkit/results/runs/1791891435785-1572397-5c629188/files/artifacts/api/task-4-01-task-engine-native.json
invocation-engine-native [engine.invocation.native]
  invocation engine-native evidence
  .testkit/results/runs/1791891435785-1572397-5c629188/files/artifacts/api/task-4-01-invocation-engine-native.json
```

The list does not read the files. To print one, name its kind and ask for text.

```console $ npx testkit artifacts src/checkout/__testkit__/refund.contract.testkit.ts --kind engine.testkit-vitest-report --format text
{"schemaVersion":1,"engineId":"node-vitest","invocationId":"1791891435785-1572397-5c629188:lease-4-2:4","taskId":"4","sourceFile":"src/checkout/__testkit__/refund.contract.testkit.ts","result":{"summary":{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":1,"numPassedTests":1,"numPendingTestSuites":0,"numPendingTests":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":1,"success":true},"failures":[],"errors":[]}}
```

The kinds depend on the engine.

| Test | Kinds worth knowing |
| --- | --- |
| Contract | `engine.testkit-vitest-report` |
| HTTP and database | `engine.summary` for the k6 summary, `engine.output` for k6's stderr |
| Database with fixtures | `testkit.dal-fixtures` for the seeded records |
| Browser, after a failure | `testkit.failure-details` and `engine.playwright-attachment` |

## Check that nothing changed

Testkit seals a run when it ends. `--verify` recomputes every checksum and compares it with the run's manifest.

```console $ npx testkit artifacts --verify
Verified 1791891435785-1572397-5c629188: 186 files (73ad31bf7d5b7b25b930cac190ed1003309dd875c391d0dc966c7e521132d0d4)
```

## Where the files are

```text title=.testkit/results/
.testkit/results/
├── latest.json
├── objects/
│   └── sha256/
└── runs/
    └── 1791891435785-1572397-5c629188/
        ├── run.json
        ├── provenance.json
        ├── manifest.json
        └── files/
            ├── artifacts/
            │   └── api/
            └── logs/
                └── api-runtime-1__api.log
```

| Path | Holds |
| --- | --- |
| `runs/<run-id>/run.json` | The run's record: its services, files, results, and timings |
| `runs/<run-id>/provenance.json` | What produced the run: the CI job, the Git commit, and the Testkit and Node versions |
| `runs/<run-id>/manifest.json` | Every file in the run and its SHA-256 checksum |
| `runs/<run-id>/files/logs/` | Service logs. `api-runtime-1__api.log` is the output of the `api` service. |
| `runs/<run-id>/files/artifacts/<service>/` | Each file's artifacts |
| `objects/sha256/` | The payloads, stored once per checksum |
| `latest.json` | A copy of the newest run's record. Read it for convenience, and treat `runs/` as the record. |

Add `.testkit/` to `.gitignore`.

## Read results from a script

`--json` prints one document for the whole run.

```sh
npx testkit run --json > run.json
```

| Key | Holds |
| --- | --- |
| `exitCode` | The exit code of the run |
| `runId` | The run's id |
| `results` | One entry per service, with its suites and files |
| `runArtifact` | The run's record. `runArtifact.summary.files` has `passed`, `failed`, `skipped`, `notRun`, and `total`. |
| `outputMode` | The reporter mode |

In Node, `@gfdelarue/testkit/workspace` exports `listRunBundles`, `loadRunBundleManifest`, `loadRunBundleArtifact`, and `verifyRunBundle`.

## Keep fewer runs

Testkit keeps every run by default. To set a limit, add [`artifacts.retention`](https://turnstone.cloud/docs/configuration.md#artifacts).

```ts title=testkit.config.ts
export default defineConfig({
	artifacts: {
		retention: {
			maxRuns: 50,
			maxAgeDays: 30,
		},
	},
});
```

## What leaves your machine

Testkit 0.4.1 has no command that uploads a run's record. The record stays in `.testkit/results` until you or your CI job copies it somewhere. [Gate merges in CI](https://turnstone.cloud/docs/ci.md#add-the-workflow) uploads it as a workflow artifact.

Two commands send a summary to GitHub or Gitea. [`testkit ci report`](https://turnstone.cloud/docs/ci.md#report-the-run-to-github-or-gitea) posts a run's result and names each failure by its file and its title. [`testkit bug file`](https://turnstone.cloud/docs/bug-reports.md#the-issue-testkit-files) files one bug as an issue.
