> ## Documentation Index
> Fetch the complete documentation index at: https://docs.garnet.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Coverage

> Which runners Garnet records, what a recorded job covers, and what happens on the platforms and jobs it does not record.

Garnet records the supported Linux runner of each job where you configured the action. What that job executes there is recorded at the kernel: the execution chains from the runner's root process to the action each performed, and the destination each outbound connection reached. Recording starts when the sensor starts, so steps above the Garnet step are not in it. The comment only knows jobs that registered a sensor. A registered job that failed to record gets a fold saying so. A job without the step, or on an unsupported runner, never registers and has no fold. That absence is not coverage of the job, and it does not mean "empty".

## Runners

| Runner                                      | What Garnet records                                          |
| :------------------------------------------ | :----------------------------------------------------------- |
| GitHub-hosted Linux x86\_64                 | What the job runs on that runner from the Garnet step onward |
| Self-hosted Linux x86\_64, systemd and sudo | What the job runs on that runner from the Garnet step onward |
| Jobs that hand work to a remote executor    | The runner's own chains and destinations                     |
| macOS, Windows, arm64                       | Not recorded                                                 |
| Linux without systemd or sudo               | Not recorded                                                 |

**GitHub-hosted Linux** is `ubuntu-latest`, `ubuntu-24.04` and `ubuntu-22.04`. This is the tested path.

**Self-hosted Linux x86\_64** works on the same terms, including managed providers such as Blacksmith and the Intel labels on Depot.

**Remote executors** run your work on their own machines: Nx Cloud agents, Turborepo or Vercel remote execution, Bazel RBE. Garnet records what the runner did, including the chains and the destinations that reach the executor.

**macOS, Windows and arm64** runners: the step logs a warning and skips; nothing is recorded there. **Linux runners without systemd or sudo**: the step warns and the job continues unrecorded.

## Requirements

Linux x86\_64, systemd, and sudo. The sensor is an eBPF binary built for amd64, installed as a systemd unit, so the step needs sudo to install it and start it.

Where a requirement is missing the step skips and your workflow continues. Errors the action catches are logged as warnings rather than failing the job.

## Coverage is per job

One Garnet step records one job. Jobs without the step produce no Execution Profile, and the PR comment carries a fold only for the jobs that have one. Add the step first in every job you want recorded. Anything that ran before the sensor started is not in the record.

A matrix job is one job per cell, and each cell needs the step to be recorded. A step guarded with `if:` on the matrix records only the cells that pass the guard: a matrix that spans Node 20, 22 and 24 on Ubuntu, macOS and Windows, with the step guarded to Node 24 on Linux, records that one cell and none of the others. The pull request comment carries one fold for it and says nothing about the rest.

## Step names

Step attribution is best effort. Each execution chain carries the workflow step that was running when its first process started, correlated from process start times and the runner's step boundaries, so some chains land under the wrong step or under `(step: "<unknown>")`. One known cause: Jibril numbers `run:` steps in file order, while GitHub numbers only the `run:` steps without an `id:`. In a job where a `run:` step declares an `id:`, that step records as `<unknown>`. Every later `run:` step without an `id:` is labelled one step early. Chains, destinations and job totals are unaffected. Read step labels inside a chain as approximate: a missing or `<unknown>` step is an attribution gap, not evidence that the step ran nothing and not background activity.

## Limits of the record

Read an Execution Profile for what it is: what Garnet recorded, not a statement that nothing else happened.

* **No completeness claim.** The record states what was captured; it does not declare that every connection was captured. Activity that starts and finishes before the sensor is up, or between the sensor's reporting intervals, can be missing from it.
* **Merge commit, not PR head.** On `pull_request` runs GitHub checks out the synthetic merge commit. The action reports your PR head SHA to Garnet, but the comment headline and the public run report currently label the run by the commit the workflow ran on — the merge commit. Aligning them to the PR head is tracked in the control plane.
* **Matrix jobs.** Jobs that share a name across a matrix can be aggregated under one fold and undercount destinations.
* **Step labels are approximate.** See [Step names](#step-names) above.
* **The post step adds time.** Stopping the sensor waits for it to flush its Execution Profile, bounded by `stop_timeout_seconds`. On busy jobs this has been observed at 1–3 minutes (about 150 s on pnpm's test job); lower the bound if post-step latency matters more than a complete flush.
* **Comparison needs a recorded pair.** The diff view appears only when the previous recorded commit on the pull request has a profile for the same job; otherwise the fold shows the current record alone.

## Fork pull requests

GitHub does not expose repository secrets or `id-token: write` to a workflow triggered from a fork, so the action has no credential there. It warns, names the missing credential in the job summary, skips recording, and the job continues. This is fail-open: an unrecorded job is not a failed job, and the pull request comment carries no fold for it. Read the absence as setup incomplete for that run, never as a job that reached nothing. Installing the [GitHub App](/security#github-app-permissions) does not change this; the credentials the runner lacks are the workflow's, not the App's.

Next: [Quick start](/quickstart) · [The Execution Profile](/execution-profile)
