What gets sent to the model

This is the question a security review asks first, so here it is precisely, per call, from the code that makes them.

Three calls, and what each carries#

CallWhat is in the promptHow many
Stage 4 — propose One changed file's diff, plus the assembled context bundle: the enclosing functions of changed lines, the callers of any changed function and, by default, their callers, and the definitions of functions the changed lines call. One per changed file
Stage 5 — verify One finding's title, category and quoted line, plus the evidence pool — the whole diff and every context block. One per candidate finding that passed the free checks
Stage 6 — summarize The verified findings only: severity, file, line, category, title and quoted line for each. No diff, no context. One per pull request

Stages 1, 2, 3 and 7 make no model call at all. Parsing, the caller search, the quote match, the fingerprinting and the ranking all happen locally.

What never leaves#

  • Files outside the diff and outside the context bundle. The rest of the repository is never read into a prompt. A file is only included if it was changed, if it contains a caller of a changed function or a caller of that caller, or if it defines a function the changed lines call. caller_depth: 1 and include_callees: false narrow that to direct callers, and --dry-run lists exactly what would be sent.
  • Your key, anywhere but the provider. It is read from the environment and used to authenticate the call to the provider you chose. It is never written to a file, never logged, and the config loader refuses to start if it finds something key-shaped in .groundtruth.yml.
  • To a server the reviewed code chooses. The endpoint cannot be set from .groundtruth.yml, because that file can be edited by the pull request under review. Only the environment and the command line — the place your key lives — can say where requests go.
  • Telemetry. There is none. There is no service operated by this project for anything to be sent to — no accounts, no hosted component, no phone-home.
The cost estimate is local. Token counting and pricing use LiteLLM's local tokenizer and its bundled price table. Estimating a review — including --dry-run — makes no network call, which is why a dry run works with no key at all.

Where it goes#

  • Straight to your provider by default. Anthropic, OpenAI, Azure, Bedrock — whichever your model names, called directly from the CI job with your key.
  • To any endpoint you name with GROUNDTRUTH_BASE_URL or --base-url — NVIDIA's API catalog, Azure, a model on your own hardware, or a self-hosted LiteLLM proxy. Point it at a proxy you run and nothing talks to a provider except infrastructure you control, which is also where org-wide spend caps and audit logs belong.
  • Nowhere at all if you point model at ollama/<model>. Every call goes to a local endpoint, no key exists, and no code leaves the machine running the review.

The optional ingest server#

Only if you set GROUNDTRUTH_INGEST_URL, which is unset by default. After the review has posted, the adapter then sends that run's result to the URL you gave: each finding's file, line, category, severity, title and quoted line of code, the summary, the model and the cost estimate. On GitHub and GitLab it also sends the fingerprints of its earlier findings with counts of the thumbs up, thumbs down and resolved threads they received; that part carries no code. The server is one you run yourself, so none of this goes to this project. Unset, nothing is sent and the adapter makes no extra calls to your platform.

Retention#

Groundtruth stores nothing. It is a process that starts, reads a diff and exits — no database, no cache, no artifacts beyond the comments it posts. The one piece of state it keeps between runs is the list of fingerprints in the summary comment on your own pull request, and a fingerprint is a SHA-1 of the file path, the surrounding code and the category — not the code itself.

What your provider retains is your provider's policy, and worth reading. If that answer is unacceptable for your codebase, the local-model path exists precisely for that case.