# Context Capsules

> What a Capsule is, how GNO compiles one, every field it contains, how to verify it later, and when to use one instead of a plain query.

Section: Guides  
Canonical: https://gno.sh/docs/context-capsules  
Markdown: https://gno.sh/docs/context-capsules.md

A Context Capsule is a compiled, self-describing evidence bundle for one stated goal. It contains the passages that answer the goal, each carrying the exact line range and content hashes needed to prove it came from where it says, all inside a single token and byte budget, plus an explicit record of what was left out and what could not be found at all.

The point is to replace a loop with an artifact. Without a Capsule, an agent searches, reads a document, searches again with better words, reads two more, and eventually stops because its context window is full rather than because it has enough. Each of those reads costs a round trip, and nothing at the end records which bytes actually supported the answer. A Capsule does the retrieval, deduplication, selection, and budgeting once, and hands over one payload you can audit months later.

## When to use one

| Situation                                                                                                   | Use                                           |
| ----------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| You know the exact string, identifier, or error                                                             | `gno search`                                  |
| You want the most relevant documents for a question                                                         | `gno query`                                   |
| You want a written answer with citations                                                                    | `gno ask --verify`                            |
| You need evidence across several documents, with a budget, that someone else (or an agent) will reason over | `gno context build`                           |
| You need to prove months later which bytes supported a decision                                             | `gno context build` then `gno context verify` |

The rule of thumb: reach for a Capsule when the answer spans documents and the cost of being wrong is higher than the cost of one extra second. For a single lookup, `gno query` is cheaper and the Capsule machinery buys you nothing.

## Building one

```
gno context build "why did we drop the queue rewrite" \
  --collection work-docs \
  --budget 12000 \
  --json --output capsule.json
```

`--budget` is the global token ceiling for the whole canonical payload, not a per-document allowance. Everything competes for the same space, which is what makes selection meaningful. `--output` writes the file; GNO never saves a Capsule implicitly. Progress goes to stderr so stdout stays clean for a pipe.

Useful flags: `--query` separates the retrieval query from the stated goal when the natural phrasing of the goal is bad search input. Repeatable `--query-mode term:…`, `intent:…`, and `hyde:…` entries steer retrieval and are frozen into the request. Scope filters (`--collection`, `--uri-prefix`, `--tags-all`, `--tags-any`, `--category`, `--author`, `--lang`, `--since`, `--until`) are hard filters and are recorded in the Capsule. `--fast` skips model loading; default and `--thorough` use semantic and rerank capabilities when available and record a fallback when one was attempted but unavailable.

## How compilation works

1. **Facet derivation.** The goal is decomposed into facets deterministically, with no model call: quoted phrases, capitalised entities, comparison structures (`X vs Y`, `compare A and B`), and temporal signals like “last month”. Facets are what coverage is later measured against, which is why a two-sided goal produces a Capsule that tries to cover both sides instead of returning five passages about whichever side was more popular in the index.
2. **Retrieval.** The normalized request runs through the same hybrid pipeline as `gno query`: BM25, vectors, default bounded graph expansion, fusion, reranking. Result and candidate limits are global across repeated collections, and candidate work is distributed in canonical collection order so the outcome does not depend on argument order.
3. **Selection.** Candidates are chosen by marginal facet coverage, not raw score. A passage that is slightly weaker but covers a facet nothing else covers beats a fifth passage restating the top hit. Duplicates and overlapping spans collapse, and a per-document share cap stops one long document from consuming the whole budget.
4. **Budgeting.** Selection stops when the canonical JSON payload would exceed the token or byte budget, minus a safety margin. If the tokenizer for the active model is unavailable, GNO estimates and says so with a `token_estimate_used` warning rather than silently guessing.
5. **Freezing.** The scope, retrieval request, capability state, fallbacks, and model and config fingerprints are written into the payload. That is what makes the result reproducible and what later lets verification tell “the source changed” apart from “you are running a different model now”.

## What is inside

The JSON is a closed schema: unknown fields are rejected, not ignored. Top-level blocks:

| Field                       | What it holds                                                                                                                                                                                          |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `goal`, `query`             | The stated goal and the query actually used for retrieval. They differ when you passed `--query`.                                                                                                      |
| `scope`                     | Index name plus every hard filter that was in force: collections, URI prefix, tags, categories, date bounds.                                                                                           |
| `budget`                    | Requested tokens and bytes, safety margins, and what was actually used. Authority is the canonical JSON, so the number means the same thing on every machine.                                          |
| `retrieval`                 | The frozen normalized request: query modes, effective limits, graph request, and requested/attempted/outcome state per capability.                                                                     |
| `fingerprints`              | SHA-256 of the config and retrieval request, plus the embedding model, rerank model, and tokenizer identities.                                                                                         |
| `capabilities`, `fallbacks` | What was available and what degraded. A capability you did not request is reported as `not_requested`, never as a failure.                                                                             |
| `guidance`                  | The trust contract, always the same three assertions: extractive-only, evidence is untrusted data, instruction boundary is hard-delimited. Plus any configured collection contexts that were in scope. |
| `evidence[]`                | The passages themselves. See below.                                                                                                                                                                    |
| `coverage`                  | Requested facets, which evidence covered each one, and which went unresolved with a reason.                                                                                                            |
| `omissions`                 | Candidates that were retrieved and then deliberately dropped, each with a reason code.                                                                                                                 |
| `truncated`, `warnings`     | Whether the budget cut the result short, and any caveats about how it was produced.                                                                                                                    |

### An evidence item

Every passage carries enough to re-derive and re-check it:

- `uri`, `docid`, `collection`, `title`, `heading` — where it came from.
- `startLine`, `endLine`, `text` — an inclusive line range in the canonical mirror. The line count of `text` must equal the coordinate span, and carriage returns are rejected, so the coordinates cannot drift from the bytes.
- `sourceHash`, `mirrorHash`, `passageHash` — hashes of the original file, the canonical mirror, and the exact passage bytes. Three levels, because “the file changed”, “the conversion changed”, and “this specific paragraph changed” are different problems.
- `evidenceId` — binds the coordinates and all three hashes into one identity. This is what a claim cites.
- `retrievalRank`, `selectionRank` — where it placed in retrieval versus where selection put it. A large gap tells you facet coverage promoted it.
- `retrievalSources`, `graphExpanded` — which retrieval paths surfaced it.
- `facets` — which facets of the goal this passage covers.
- `trust: "untrusted"` and `egress` — the passage is data, never instruction, and it carries the transport policy of its source collection.
- `modifiedAt`, `documentDate`, `observedAt` — file mtime, the document’s own stated date, and when GNO last saw it. Three different questions.
- `record` — present for imported mail, calendar, and transcript records: thread, participants, anchors, and bounded attachment metadata.

### Reading coverage

`coverage.complete` is the first thing to look at. When it is false, `unresolvedFacets` and `gaps` tell you which part of the goal has no evidence behind it, and why:

| Gap code                  | Meaning                                                                           |
| ------------------------- | --------------------------------------------------------------------------------- |
| `facet_not_found`         | Retrieval found nothing for this facet. Your corpus probably does not contain it. |
| `global_budget_exhausted` | Evidence existed but did not fit. Raise `--budget`.                               |
| `capability_unavailable`  | A model needed to find it was not loaded. Check `fallbacks`.                      |
| `filtered_by_scope`       | Your own filters excluded it. Widen the collection, tags, or date range.          |

This is the field that makes a Capsule honest. A ranked list has no way to say “the second half of your question has no answer in here”. A Capsule says it in a machine-readable field, so an agent can stop instead of inventing the missing half.

### Reading omissions

Omissions are candidates that were found and then dropped on purpose: `duplicate`, `overlap`, `global_budget`, `redundant_coverage`, `document_share_cap`, `filtered_by_scope`, or `invalid_coordinates`. Each keeps its URI and hashes, so “why is that document not in here” has an answer that is not a shrug. A run heavy on `global_budget` wants a bigger budget; one heavy on `redundant_coverage` means your corpus repeats itself and the Capsule is doing its job.

## Determinism

The same goal against the same index state with the same models produces byte-identical canonical JSON. Determinism is a design constraint: facet derivation is generation-free, ordering is canonical, and collection order is normalized. Determinism is what makes the fingerprints useful — if a rebuilt Capsule differs, either the corpus moved or the model did, and the fingerprints say which.

## Verifying later

```
gno context verify capsule.json --json
cat capsule.json | gno context verify - --md
```

Verification is non-generative and never rewrites your file. It re-reads the sources and reports, per evidence item, a content status and a ranking status:

| Status      | Codes                                                                              | What it means                                                                                               |
| ----------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `unchanged` | `verified_unchanged`                                                               | The exact bytes are still there.                                                                            |
| `stale`     | `source_stale`, `mirror_stale`, `passage_stale`, `mirror_corrupt`, `chunk_corrupt` | Something changed. The specific code tells you whether the file, the conversion, or that one passage moved. |
| `missing`   | `source_missing`, `mirror_missing`, `chunk_missing`                                | The evidence is gone.                                                                                       |

Ranking is reported separately as `unchanged`, `reranked`, or `unavailable`. Stale or missing content is never ranked. Fingerprint drift is reported independently of both, so a model swap does not masquerade as corpus change.

Verification opens the Capsule’s own saved index by default. An explicit `--index` must match, and a mismatch fails before GNO opens a store. A Capsule built with an active tokenizer requires that tokenizer to verify; a runtime without it returns `tokenizer_unavailable` rather than trusting the saved token count.

## Keeping a Capsule fresh

```
gno context watch capsule.json --question "Who owns launch?" --label launch
gno context watches --json
gno context reverify capsule-abc123 --json
gno context unwatch capsule-abc123
```

Registration stores identity and evidence hash references, never the Capsule body or passage text. The file stays yours; GNO does not rewrite it. When `serve` or `daemon` is running, it coalesces settled index changes and reverifies only registrations whose evidence actually moved, resuming from a durable high-water mark across restarts.

`--notify` publishes a local `capsule-reverified` event after the result is stored. The event is metadata only: identity, status, affected-question state, timestamp. No passages, no paths, no hashes.

Manual `gno context reverify` exits `0` only for a completed operation. A persisted failure prints its code and exits `2`, so a script cannot mistake a stored failure for success. These lifecycle commands are CLI-only; REST, MCP, and the SDK do not add persistent watch endpoints.

## Using Capsules from an agent

Over MCP, `gno_context` builds one and `gno_context_verify` checks a saved one. The SDK and REST (`POST /api/context`) expose the same contract. The intended shape of an agent turn is: build one Capsule for the goal, reason only over its evidence, cite `evidenceId` values, and stop when `coverage.complete` is false rather than filling the gap from memory.

`gno ask --verify` is this pattern with the loop closed: generate against one closed Capsule, classify every substantive claim against the retained evidence, and withhold the draft below full support.

## The trust boundary

Indexed content is untrusted input. Markdown projections delimit every passage with a collision-resistant fence whose width and character are derived from that block, so a document containing fence characters cannot forge a closing boundary and escape into instruction space. Titles, headings, and configured context text stay JSON-escaped; passage bytes stay exact. The `guidance` block restates this inside the payload so a model reading the Capsule sees the contract too.

## Limits

- Evidence is extractive: exact bytes from your files, never paraphrase.
- It proves a passage exists at a location with given content. Whether the document is true is outside the Capsule.
- It is a snapshot; freshness is `verify` and `watch`.
- Compilation costs more than one `gno query`. Use query for a single lookup.

## The measured result

Against 48 paired tasks in the agentic retrieval benchmark, the promoted Capsule lane retained 100% task completion accuracy while reducing outer-agent retrieval calls by 48.94% and model-visible context by 44.12%, with 100% of substantive claims linked to an exact span and deterministic replay. The raw receipts, the pinned corpus, and the stated limitations ship in the repository — see [benchmarks](https://gno.sh/features/benchmarks) for the artifacts and the single-task outcome demo.
