Frames, relations, and honest budgets
A frame is not a string. It is a structured record of relevance, cost, provenance, and validity — and the graph is carried in its labelled relations.
Frame kinds
Every frame declares one of seven kinds, serialized in snake_case on the wire:
| Kind | Typical content |
|---|---|
| snippet | A contiguous run of source or text, e.g. a function body. |
| symbol | A named program entity — its signature, definition site. |
| fact | A discrete assertion with validity bounds. |
| doc | Documentation or prose, whole or excerpted. |
| memory | Durable agent memory: preferences, learned constraints. |
| episode | A record of something that happened — an incident, a session. |
| graph | A node with its labelled edges — structure itself as context. |
Representations: content-optional by design
A frame carries its content in one of three representations, negotiated through the query’s representation_preferences and the provider’s declared capabilities:
full— inlinecontentrequired. The default: afullframe omits therepresentationfield entirely, so pre-representation frames are unchanged on the wire.compact— inline content that is a transformed rendering (say, a signature-only view of a file), plus thecanonical_content_hashof the source it was derived from, atransformidentity, and acontent_refback to the original.reference— no inline content at all: an opaquecontent_refresolver handle plus the canonical hash. It is never encoded ascontent: ""; the field is omitted entirely.
Supporting fields make fidelity explicit: content_fidelity is one of exact, normalized, summarized, omitted; a host can demand a floor with minimum_content_fidelity or require inline bytes with inline_content_requirement.
Relations: the graph itself
CGP is named for the graph, and the graph is carried in relations: a graph frame is a node with its labelled edges, not an ad-hoc serialization format. Each relation is a directional edge { rel, target_uri, display_name? }. Six names are published — code.calls, code.imports, code.defines, code.references, doc.documents, episode.follows — and the vocabulary is open: providers namespace their own (myindex.owns), and a host must not reject unknown values.
Queries meet the graph through anchors: URIs the host considers focal — open files, mentioned symbols. A graph-capable provider should boost frames within a small number of relation hops of an anchor.
Token accounting
A budget token is an accounting unit, not a tokenizer. The canonical rule:
token_cost = ceil( utf8_byte_length(content) / 4 )Because the rule is deterministic in every language, budget honesty is machine-checkable: summed token_cost must not exceed the query’s max_tokens (B1), frame count must not exceed max_frames (B4), and a host must drop — with a loud report — the frames of any provider that violates the budget (B2).
A host must not treat one budget token as one model token. The 4-byte rule tracks English prose well but under-estimates dense source code (~3–3.5 bytes per model token) and CJK text (~3), so hosts map real budgets through a safety factor — the reference host suggests 1.35: 10,000 model tokens of headroom becomes a 7,407 budget-token query. The count covers content only, never titles, citation labels, or host chrome.
Identity and time
The stable identity of a frame’s exact bytes is the triple (provider_id, frame_id, content_digest) — the spine shared by deterministic composition, usage reports, and verification. A frame may omit content_digest, but such a frame is not verifiable, and a host must re-query rather than reuse it.
Frames are bi-temporal: valid_from / valid_to bound when the content was true; recorded_at says when it was captured; and a query’s as_of pins retrieval to an instant.