Skip to main content
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

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. The action logs errors it catches 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. For example, 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. Some chains can land under the wrong section. 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:, its attribution is recorded as <unknown>. Every later run: step without an id: is labeled one step early. Chains, destinations, and job totals are unaffected. Treat placement as approximate. The chain was recorded; only its placement is uncertain. A misplaced chain is not evidence that the step ran nothing or was runner or GitHub traffic.

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. That commit is 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 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 uses a recorded history. Each job is compared with the union of its last 10 recorded runs on the base branch. If fewer runs exist, Garnet names how many it used; with fewer than two, it uses the PR base alone. If no base record exists, it uses the previous push only when that commit is part of the pull request. Marker counts mirror rendered +/− workload rows, including removals. Only the headline outcome follows novelty: a destination is new only when no baseline run reached it, and removals never change the outcome.

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. Since v2.4.0 the job is still recorded: Jibril runs locally, nothing is sent from the runner, and the post step uploads the record as a garnet-run-* workflow artifact. The GitHub App collects that artifact with its own credentials once the workflow finishes and posts the Runtime Review from it. A fork pull request gets a comment only when the App is installed on the repository. Two limits apply. First, Garnet does not apply managed network policies on these runs, because the runner never talks to the control plane. Second, events other than a pull request (push, workflow_dispatch, a schedule) still skip recording with a warning that names the missing credential, and the job continues. An unrecorded job is not a failed job, and the comment carries no fold for it. Read the absence as setup incomplete for that run, never as a job that reached nothing. Next: Quick start · The Execution Profile